blob: e05f7b82b952e1c4cbc2cb402102f7f4afa3a029 [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
Florent Xiclunadc692742010-08-15 20:16:27 +000043from test.support import captured_stdout, run_with_locale, run_unittest
Christian Heimes180510d2008-03-03 19:15:45 +000044import textwrap
Christian Heimes180510d2008-03-03 19:15:45 +000045import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000046import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000047import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000048try:
49 import threading
50except ImportError:
51 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000052
53
Christian Heimes180510d2008-03-03 19:15:45 +000054class BaseTest(unittest.TestCase):
55
56 """Base class for logging tests."""
57
58 log_format = "%(name)s -> %(levelname)s: %(message)s"
59 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
60 message_num = 0
61
62 def setUp(self):
63 """Setup the default logging stream to an internal StringIO instance,
64 so that we can examine log output as we want."""
65 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000066 logging._acquireLock()
67 try:
Christian Heimes180510d2008-03-03 19:15:45 +000068 self.saved_handlers = logging._handlers.copy()
69 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000070 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000071 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000072 self.logger_states = logger_states = {}
73 for name in saved_loggers:
74 logger_states[name] = getattr(saved_loggers[name],
75 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000076 finally:
77 logging._releaseLock()
78
Benjamin Peterson22005fc2010-04-11 16:25:06 +000079 # Set two unused loggers: one non-ASCII and one Unicode.
80 # This is to test correct operation when sorting existing
81 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000082 self.logger1 = logging.getLogger("\xab\xd7\xbb")
83 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000084
Christian Heimes180510d2008-03-03 19:15:45 +000085 self.root_logger = logging.getLogger("")
86 self.original_logging_level = self.root_logger.getEffectiveLevel()
87
88 self.stream = io.StringIO()
89 self.root_logger.setLevel(logging.DEBUG)
90 self.root_hdlr = logging.StreamHandler(self.stream)
91 self.root_formatter = logging.Formatter(self.log_format)
92 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +000093 if self.logger1.hasHandlers():
94 hlist = self.logger1.handlers + self.root_logger.handlers
95 raise AssertionError('Unexpected handlers: %s' % hlist)
96 if self.logger2.hasHandlers():
97 hlist = self.logger2.handlers + self.root_logger.handlers
98 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +000099 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000100 self.assertTrue(self.logger1.hasHandlers())
101 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000102
103 def tearDown(self):
104 """Remove our logging stream, and restore the original logging
105 level."""
106 self.stream.close()
107 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000108 while self.root_logger.handlers:
109 h = self.root_logger.handlers[0]
110 self.root_logger.removeHandler(h)
111 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000112 self.root_logger.setLevel(self.original_logging_level)
113 logging._acquireLock()
114 try:
115 logging._levelNames.clear()
116 logging._levelNames.update(self.saved_level_names)
117 logging._handlers.clear()
118 logging._handlers.update(self.saved_handlers)
119 logging._handlerList[:] = self.saved_handler_list
120 loggerDict = logging.getLogger().manager.loggerDict
121 loggerDict.clear()
122 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000123 logger_states = self.logger_states
124 for name in self.logger_states:
125 if logger_states[name] is not None:
126 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000127 finally:
128 logging._releaseLock()
129
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000130 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000131 """Match the collected log lines against the regular expression
132 self.expected_log_pat, and compare the extracted group values to
133 the expected_values list of tuples."""
134 stream = stream or self.stream
135 pat = re.compile(self.expected_log_pat)
136 try:
137 stream.reset()
138 actual_lines = stream.readlines()
139 except AttributeError:
140 # StringIO.StringIO lacks a reset() method.
141 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000142 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000143 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000144 for actual, expected in zip(actual_lines, expected_values):
145 match = pat.search(actual)
146 if not match:
147 self.fail("Log line does not match expected pattern:\n" +
148 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000149 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000150 s = stream.read()
151 if s:
152 self.fail("Remaining output at end of log stream:\n" + s)
153
154 def next_message(self):
155 """Generate a message consisting solely of an auto-incrementing
156 integer."""
157 self.message_num += 1
158 return "%d" % self.message_num
159
160
161class BuiltinLevelsTest(BaseTest):
162 """Test builtin levels and their inheritance."""
163
164 def test_flat(self):
165 #Logging levels in a flat logger namespace.
166 m = self.next_message
167
168 ERR = logging.getLogger("ERR")
169 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000170 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000171 INF.setLevel(logging.INFO)
172 DEB = logging.getLogger("DEB")
173 DEB.setLevel(logging.DEBUG)
174
175 # These should log.
176 ERR.log(logging.CRITICAL, m())
177 ERR.error(m())
178
179 INF.log(logging.CRITICAL, m())
180 INF.error(m())
181 INF.warn(m())
182 INF.info(m())
183
184 DEB.log(logging.CRITICAL, m())
185 DEB.error(m())
186 DEB.warn (m())
187 DEB.info (m())
188 DEB.debug(m())
189
190 # These should not log.
191 ERR.warn(m())
192 ERR.info(m())
193 ERR.debug(m())
194
195 INF.debug(m())
196
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000197 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000198 ('ERR', 'CRITICAL', '1'),
199 ('ERR', 'ERROR', '2'),
200 ('INF', 'CRITICAL', '3'),
201 ('INF', 'ERROR', '4'),
202 ('INF', 'WARNING', '5'),
203 ('INF', 'INFO', '6'),
204 ('DEB', 'CRITICAL', '7'),
205 ('DEB', 'ERROR', '8'),
206 ('DEB', 'WARNING', '9'),
207 ('DEB', 'INFO', '10'),
208 ('DEB', 'DEBUG', '11'),
209 ])
210
211 def test_nested_explicit(self):
212 # Logging levels in a nested namespace, all explicitly set.
213 m = self.next_message
214
215 INF = logging.getLogger("INF")
216 INF.setLevel(logging.INFO)
217 INF_ERR = logging.getLogger("INF.ERR")
218 INF_ERR.setLevel(logging.ERROR)
219
220 # These should log.
221 INF_ERR.log(logging.CRITICAL, m())
222 INF_ERR.error(m())
223
224 # These should not log.
225 INF_ERR.warn(m())
226 INF_ERR.info(m())
227 INF_ERR.debug(m())
228
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000229 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000230 ('INF.ERR', 'CRITICAL', '1'),
231 ('INF.ERR', 'ERROR', '2'),
232 ])
233
234 def test_nested_inherited(self):
235 #Logging levels in a nested namespace, inherited from parent loggers.
236 m = self.next_message
237
238 INF = logging.getLogger("INF")
239 INF.setLevel(logging.INFO)
240 INF_ERR = logging.getLogger("INF.ERR")
241 INF_ERR.setLevel(logging.ERROR)
242 INF_UNDEF = logging.getLogger("INF.UNDEF")
243 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
244 UNDEF = logging.getLogger("UNDEF")
245
246 # These should log.
247 INF_UNDEF.log(logging.CRITICAL, m())
248 INF_UNDEF.error(m())
249 INF_UNDEF.warn(m())
250 INF_UNDEF.info(m())
251 INF_ERR_UNDEF.log(logging.CRITICAL, m())
252 INF_ERR_UNDEF.error(m())
253
254 # These should not log.
255 INF_UNDEF.debug(m())
256 INF_ERR_UNDEF.warn(m())
257 INF_ERR_UNDEF.info(m())
258 INF_ERR_UNDEF.debug(m())
259
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000260 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000261 ('INF.UNDEF', 'CRITICAL', '1'),
262 ('INF.UNDEF', 'ERROR', '2'),
263 ('INF.UNDEF', 'WARNING', '3'),
264 ('INF.UNDEF', 'INFO', '4'),
265 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
266 ('INF.ERR.UNDEF', 'ERROR', '6'),
267 ])
268
269 def test_nested_with_virtual_parent(self):
270 # Logging levels when some parent does not exist yet.
271 m = self.next_message
272
273 INF = logging.getLogger("INF")
274 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
275 CHILD = logging.getLogger("INF.BADPARENT")
276 INF.setLevel(logging.INFO)
277
278 # These should log.
279 GRANDCHILD.log(logging.FATAL, m())
280 GRANDCHILD.info(m())
281 CHILD.log(logging.FATAL, m())
282 CHILD.info(m())
283
284 # These should not log.
285 GRANDCHILD.debug(m())
286 CHILD.debug(m())
287
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000288 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000289 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
290 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
291 ('INF.BADPARENT', 'CRITICAL', '3'),
292 ('INF.BADPARENT', 'INFO', '4'),
293 ])
294
295
296class BasicFilterTest(BaseTest):
297
298 """Test the bundled Filter class."""
299
300 def test_filter(self):
301 # Only messages satisfying the specified criteria pass through the
302 # filter.
303 filter_ = logging.Filter("spam.eggs")
304 handler = self.root_logger.handlers[0]
305 try:
306 handler.addFilter(filter_)
307 spam = logging.getLogger("spam")
308 spam_eggs = logging.getLogger("spam.eggs")
309 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
310 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
311
312 spam.info(self.next_message())
313 spam_eggs.info(self.next_message()) # Good.
314 spam_eggs_fish.info(self.next_message()) # Good.
315 spam_bakedbeans.info(self.next_message())
316
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000317 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000318 ('spam.eggs', 'INFO', '2'),
319 ('spam.eggs.fish', 'INFO', '3'),
320 ])
321 finally:
322 handler.removeFilter(filter_)
323
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000324 def test_callable_filter(self):
325 # Only messages satisfying the specified criteria pass through the
326 # filter.
327
328 def filterfunc(record):
329 parts = record.name.split('.')
330 prefix = '.'.join(parts[:2])
331 return prefix == 'spam.eggs'
332
333 handler = self.root_logger.handlers[0]
334 try:
335 handler.addFilter(filterfunc)
336 spam = logging.getLogger("spam")
337 spam_eggs = logging.getLogger("spam.eggs")
338 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
339 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
340
341 spam.info(self.next_message())
342 spam_eggs.info(self.next_message()) # Good.
343 spam_eggs_fish.info(self.next_message()) # Good.
344 spam_bakedbeans.info(self.next_message())
345
346 self.assert_log_lines([
347 ('spam.eggs', 'INFO', '2'),
348 ('spam.eggs.fish', 'INFO', '3'),
349 ])
350 finally:
351 handler.removeFilter(filterfunc)
352
Christian Heimes180510d2008-03-03 19:15:45 +0000353
354#
355# First, we define our levels. There can be as many as you want - the only
356# limitations are that they should be integers, the lowest should be > 0 and
357# larger values mean less information being logged. If you need specific
358# level values which do not fit into these limitations, you can use a
359# mapping dictionary to convert between your application levels and the
360# logging system.
361#
362SILENT = 120
363TACITURN = 119
364TERSE = 118
365EFFUSIVE = 117
366SOCIABLE = 116
367VERBOSE = 115
368TALKATIVE = 114
369GARRULOUS = 113
370CHATTERBOX = 112
371BORING = 111
372
373LEVEL_RANGE = range(BORING, SILENT + 1)
374
375#
376# Next, we define names for our levels. You don't need to do this - in which
377# case the system will use "Level n" to denote the text for the level.
378#
379my_logging_levels = {
380 SILENT : 'Silent',
381 TACITURN : 'Taciturn',
382 TERSE : 'Terse',
383 EFFUSIVE : 'Effusive',
384 SOCIABLE : 'Sociable',
385 VERBOSE : 'Verbose',
386 TALKATIVE : 'Talkative',
387 GARRULOUS : 'Garrulous',
388 CHATTERBOX : 'Chatterbox',
389 BORING : 'Boring',
390}
391
392class GarrulousFilter(logging.Filter):
393
394 """A filter which blocks garrulous messages."""
395
396 def filter(self, record):
397 return record.levelno != GARRULOUS
398
399class VerySpecificFilter(logging.Filter):
400
401 """A filter which blocks sociable and taciturn messages."""
402
403 def filter(self, record):
404 return record.levelno not in [SOCIABLE, TACITURN]
405
406
407class CustomLevelsAndFiltersTest(BaseTest):
408
409 """Test various filtering possibilities with custom logging levels."""
410
411 # Skip the logger name group.
412 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
413
414 def setUp(self):
415 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000416 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000417 logging.addLevelName(k, v)
418
419 def log_at_all_levels(self, logger):
420 for lvl in LEVEL_RANGE:
421 logger.log(lvl, self.next_message())
422
423 def test_logger_filter(self):
424 # Filter at logger level.
425 self.root_logger.setLevel(VERBOSE)
426 # Levels >= 'Verbose' are good.
427 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000428 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000429 ('Verbose', '5'),
430 ('Sociable', '6'),
431 ('Effusive', '7'),
432 ('Terse', '8'),
433 ('Taciturn', '9'),
434 ('Silent', '10'),
435 ])
436
437 def test_handler_filter(self):
438 # Filter at handler level.
439 self.root_logger.handlers[0].setLevel(SOCIABLE)
440 try:
441 # Levels >= 'Sociable' are good.
442 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000443 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000444 ('Sociable', '6'),
445 ('Effusive', '7'),
446 ('Terse', '8'),
447 ('Taciturn', '9'),
448 ('Silent', '10'),
449 ])
450 finally:
451 self.root_logger.handlers[0].setLevel(logging.NOTSET)
452
453 def test_specific_filters(self):
454 # Set a specific filter object on the handler, and then add another
455 # filter object on the logger itself.
456 handler = self.root_logger.handlers[0]
457 specific_filter = None
458 garr = GarrulousFilter()
459 handler.addFilter(garr)
460 try:
461 self.log_at_all_levels(self.root_logger)
462 first_lines = [
463 # Notice how 'Garrulous' is missing
464 ('Boring', '1'),
465 ('Chatterbox', '2'),
466 ('Talkative', '4'),
467 ('Verbose', '5'),
468 ('Sociable', '6'),
469 ('Effusive', '7'),
470 ('Terse', '8'),
471 ('Taciturn', '9'),
472 ('Silent', '10'),
473 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000474 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000475
476 specific_filter = VerySpecificFilter()
477 self.root_logger.addFilter(specific_filter)
478 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000479 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000480 # Not only 'Garrulous' is still missing, but also 'Sociable'
481 # and 'Taciturn'
482 ('Boring', '11'),
483 ('Chatterbox', '12'),
484 ('Talkative', '14'),
485 ('Verbose', '15'),
486 ('Effusive', '17'),
487 ('Terse', '18'),
488 ('Silent', '20'),
489 ])
490 finally:
491 if specific_filter:
492 self.root_logger.removeFilter(specific_filter)
493 handler.removeFilter(garr)
494
495
496class MemoryHandlerTest(BaseTest):
497
498 """Tests for the MemoryHandler."""
499
500 # Do not bother with a logger name group.
501 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
502
503 def setUp(self):
504 BaseTest.setUp(self)
505 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
506 self.root_hdlr)
507 self.mem_logger = logging.getLogger('mem')
508 self.mem_logger.propagate = 0
509 self.mem_logger.addHandler(self.mem_hdlr)
510
511 def tearDown(self):
512 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000513 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000514
515 def test_flush(self):
516 # The memory handler flushes to its target handler based on specific
517 # criteria (message count and message level).
518 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000519 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000520 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000521 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000522 # This will flush because the level is >= logging.WARNING
523 self.mem_logger.warn(self.next_message())
524 lines = [
525 ('DEBUG', '1'),
526 ('INFO', '2'),
527 ('WARNING', '3'),
528 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000529 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000530 for n in (4, 14):
531 for i in range(9):
532 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000533 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000534 # This will flush because it's the 10th message since the last
535 # flush.
536 self.mem_logger.debug(self.next_message())
537 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000538 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000539
540 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000541 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000542
543
544class ExceptionFormatter(logging.Formatter):
545 """A special exception formatter."""
546 def formatException(self, ei):
547 return "Got a [%s]" % ei[0].__name__
548
549
550class ConfigFileTest(BaseTest):
551
552 """Reading logging config from a .ini-style config file."""
553
554 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
555
556 # config0 is a standard configuration.
557 config0 = """
558 [loggers]
559 keys=root
560
561 [handlers]
562 keys=hand1
563
564 [formatters]
565 keys=form1
566
567 [logger_root]
568 level=WARNING
569 handlers=hand1
570
571 [handler_hand1]
572 class=StreamHandler
573 level=NOTSET
574 formatter=form1
575 args=(sys.stdout,)
576
577 [formatter_form1]
578 format=%(levelname)s ++ %(message)s
579 datefmt=
580 """
581
582 # config1 adds a little to the standard configuration.
583 config1 = """
584 [loggers]
585 keys=root,parser
586
587 [handlers]
588 keys=hand1
589
590 [formatters]
591 keys=form1
592
593 [logger_root]
594 level=WARNING
595 handlers=
596
597 [logger_parser]
598 level=DEBUG
599 handlers=hand1
600 propagate=1
601 qualname=compiler.parser
602
603 [handler_hand1]
604 class=StreamHandler
605 level=NOTSET
606 formatter=form1
607 args=(sys.stdout,)
608
609 [formatter_form1]
610 format=%(levelname)s ++ %(message)s
611 datefmt=
612 """
613
Vinay Sajip3f84b072011-03-07 17:49:33 +0000614 # config1a moves the handler to the root.
615 config1a = """
616 [loggers]
617 keys=root,parser
618
619 [handlers]
620 keys=hand1
621
622 [formatters]
623 keys=form1
624
625 [logger_root]
626 level=WARNING
627 handlers=hand1
628
629 [logger_parser]
630 level=DEBUG
631 handlers=
632 propagate=1
633 qualname=compiler.parser
634
635 [handler_hand1]
636 class=StreamHandler
637 level=NOTSET
638 formatter=form1
639 args=(sys.stdout,)
640
641 [formatter_form1]
642 format=%(levelname)s ++ %(message)s
643 datefmt=
644 """
645
Christian Heimes180510d2008-03-03 19:15:45 +0000646 # config2 has a subtle configuration error that should be reported
647 config2 = config1.replace("sys.stdout", "sys.stbout")
648
649 # config3 has a less subtle configuration error
650 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
651
652 # config4 specifies a custom formatter class to be loaded
653 config4 = """
654 [loggers]
655 keys=root
656
657 [handlers]
658 keys=hand1
659
660 [formatters]
661 keys=form1
662
663 [logger_root]
664 level=NOTSET
665 handlers=hand1
666
667 [handler_hand1]
668 class=StreamHandler
669 level=NOTSET
670 formatter=form1
671 args=(sys.stdout,)
672
673 [formatter_form1]
674 class=""" + __name__ + """.ExceptionFormatter
675 format=%(levelname)s:%(name)s:%(message)s
676 datefmt=
677 """
678
Georg Brandl3dbca812008-07-23 16:10:53 +0000679 # config5 specifies a custom handler class to be loaded
680 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
681
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000682 # config6 uses ', ' delimiters in the handlers and formatters sections
683 config6 = """
684 [loggers]
685 keys=root,parser
686
687 [handlers]
688 keys=hand1, hand2
689
690 [formatters]
691 keys=form1, form2
692
693 [logger_root]
694 level=WARNING
695 handlers=
696
697 [logger_parser]
698 level=DEBUG
699 handlers=hand1
700 propagate=1
701 qualname=compiler.parser
702
703 [handler_hand1]
704 class=StreamHandler
705 level=NOTSET
706 formatter=form1
707 args=(sys.stdout,)
708
709 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000710 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000711 level=NOTSET
712 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000713 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000714
715 [formatter_form1]
716 format=%(levelname)s ++ %(message)s
717 datefmt=
718
719 [formatter_form2]
720 format=%(message)s
721 datefmt=
722 """
723
Vinay Sajip3f84b072011-03-07 17:49:33 +0000724 # config7 adds a compiler logger.
725 config7 = """
726 [loggers]
727 keys=root,parser,compiler
728
729 [handlers]
730 keys=hand1
731
732 [formatters]
733 keys=form1
734
735 [logger_root]
736 level=WARNING
737 handlers=hand1
738
739 [logger_compiler]
740 level=DEBUG
741 handlers=
742 propagate=1
743 qualname=compiler
744
745 [logger_parser]
746 level=DEBUG
747 handlers=
748 propagate=1
749 qualname=compiler.parser
750
751 [handler_hand1]
752 class=StreamHandler
753 level=NOTSET
754 formatter=form1
755 args=(sys.stdout,)
756
757 [formatter_form1]
758 format=%(levelname)s ++ %(message)s
759 datefmt=
760 """
761
Christian Heimes180510d2008-03-03 19:15:45 +0000762 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000763 file = io.StringIO(textwrap.dedent(conf))
764 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000765
766 def test_config0_ok(self):
767 # A simple config file which overrides the default settings.
768 with captured_stdout() as output:
769 self.apply_config(self.config0)
770 logger = logging.getLogger()
771 # Won't output anything
772 logger.info(self.next_message())
773 # Outputs a message
774 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000775 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000776 ('ERROR', '2'),
777 ], stream=output)
778 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000779 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000780
Georg Brandl3dbca812008-07-23 16:10:53 +0000781 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000782 # A config file defining a sub-parser as well.
783 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000784 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000785 logger = logging.getLogger("compiler.parser")
786 # Both will output a message
787 logger.info(self.next_message())
788 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000789 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000790 ('INFO', '1'),
791 ('ERROR', '2'),
792 ], stream=output)
793 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000794 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000795
796 def test_config2_failure(self):
797 # A simple config file which overrides the default settings.
798 self.assertRaises(Exception, self.apply_config, self.config2)
799
800 def test_config3_failure(self):
801 # A simple config file which overrides the default settings.
802 self.assertRaises(Exception, self.apply_config, self.config3)
803
804 def test_config4_ok(self):
805 # A config file specifying a custom formatter class.
806 with captured_stdout() as output:
807 self.apply_config(self.config4)
808 logger = logging.getLogger()
809 try:
810 raise RuntimeError()
811 except RuntimeError:
812 logging.exception("just testing")
813 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000814 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000815 "ERROR:root:just testing\nGot a [RuntimeError]\n")
816 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000817 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000818
Georg Brandl3dbca812008-07-23 16:10:53 +0000819 def test_config5_ok(self):
820 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000821
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000822 def test_config6_ok(self):
823 self.test_config1_ok(config=self.config6)
824
Vinay Sajip3f84b072011-03-07 17:49:33 +0000825 def test_config7_ok(self):
826 with captured_stdout() as output:
827 self.apply_config(self.config1a)
828 logger = logging.getLogger("compiler.parser")
829 # See issue #11424. compiler-hyphenated sorts
830 # between compiler and compiler.xyz and this
831 # was preventing compiler.xyz from being included
832 # in the child loggers of compiler because of an
833 # overzealous loop termination condition.
834 hyphenated = logging.getLogger('compiler-hyphenated')
835 # All will output a message
836 logger.info(self.next_message())
837 logger.error(self.next_message())
838 hyphenated.critical(self.next_message())
839 self.assert_log_lines([
840 ('INFO', '1'),
841 ('ERROR', '2'),
842 ('CRITICAL', '3'),
843 ], stream=output)
844 # Original logger output is empty.
845 self.assert_log_lines([])
846 with captured_stdout() as output:
847 self.apply_config(self.config7)
848 logger = logging.getLogger("compiler.parser")
849 self.assertFalse(logger.disabled)
850 # Both will output a message
851 logger.info(self.next_message())
852 logger.error(self.next_message())
853 logger = logging.getLogger("compiler.lexer")
854 # Both will output a message
855 logger.info(self.next_message())
856 logger.error(self.next_message())
857 # Will not appear
858 hyphenated.critical(self.next_message())
859 self.assert_log_lines([
860 ('INFO', '4'),
861 ('ERROR', '5'),
862 ('INFO', '6'),
863 ('ERROR', '7'),
864 ], stream=output)
865 # Original logger output is empty.
866 self.assert_log_lines([])
867
Christian Heimes180510d2008-03-03 19:15:45 +0000868class LogRecordStreamHandler(StreamRequestHandler):
869
870 """Handler for a streaming logging request. It saves the log message in the
871 TCP server's 'log_output' attribute."""
872
873 TCP_LOG_END = "!!!END!!!"
874
875 def handle(self):
876 """Handle multiple requests - each expected to be of 4-byte length,
877 followed by the LogRecord in pickle format. Logs the record
878 according to whatever policy is configured locally."""
879 while True:
880 chunk = self.connection.recv(4)
881 if len(chunk) < 4:
882 break
883 slen = struct.unpack(">L", chunk)[0]
884 chunk = self.connection.recv(slen)
885 while len(chunk) < slen:
886 chunk = chunk + self.connection.recv(slen - len(chunk))
887 obj = self.unpickle(chunk)
888 record = logging.makeLogRecord(obj)
889 self.handle_log_record(record)
890
891 def unpickle(self, data):
892 return pickle.loads(data)
893
894 def handle_log_record(self, record):
895 # If the end-of-messages sentinel is seen, tell the server to
896 # terminate.
897 if self.TCP_LOG_END in record.msg:
898 self.server.abort = 1
899 return
900 self.server.log_output += record.msg + "\n"
901
Guido van Rossum376e6362003-04-25 14:22:00 +0000902
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000903class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +0000904
905 """A simple-minded TCP socket-based logging receiver suitable for test
906 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000907
908 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +0000909 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000910
911 def __init__(self, host='localhost',
912 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
913 handler=LogRecordStreamHandler):
914 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +0000915 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +0000916 self.timeout = 0.1
917 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000918
919 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000920 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000921 rd, wr, ex = select.select([self.socket.fileno()], [], [],
922 self.timeout)
923 if rd:
924 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +0000925 # Notify the main thread that we're about to exit
926 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000927 # close the listen socket
928 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000929
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000930
Victor Stinner45df8202010-04-28 22:31:17 +0000931@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +0000932class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000933
Christian Heimes180510d2008-03-03 19:15:45 +0000934 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000935
Christian Heimes180510d2008-03-03 19:15:45 +0000936 def setUp(self):
937 """Set up a TCP server to receive log messages, and a SocketHandler
938 pointing to that server's address and port."""
939 BaseTest.setUp(self)
940 self.tcpserver = LogRecordSocketReceiver(port=0)
941 self.port = self.tcpserver.socket.getsockname()[1]
942 self.threads = [
943 threading.Thread(target=self.tcpserver.serve_until_stopped)]
944 for thread in self.threads:
945 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000946
Christian Heimes180510d2008-03-03 19:15:45 +0000947 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
948 self.sock_hdlr.setFormatter(self.root_formatter)
949 self.root_logger.removeHandler(self.root_logger.handlers[0])
950 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000951
Christian Heimes180510d2008-03-03 19:15:45 +0000952 def tearDown(self):
953 """Shutdown the TCP server."""
954 try:
955 self.tcpserver.abort = True
956 del self.tcpserver
957 self.root_logger.removeHandler(self.sock_hdlr)
958 self.sock_hdlr.close()
959 for thread in self.threads:
960 thread.join(2.0)
961 finally:
962 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000963
Christian Heimes180510d2008-03-03 19:15:45 +0000964 def get_output(self):
965 """Get the log output as received by the TCP server."""
966 # Signal the TCP receiver and wait for it to terminate.
967 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
968 self.tcpserver.finished.wait(2.0)
969 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000970
Christian Heimes180510d2008-03-03 19:15:45 +0000971 def test_output(self):
972 # The log message sent to the SocketHandler is properly received.
973 logger = logging.getLogger("tcp")
974 logger.error("spam")
975 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000976 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000977
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000978
Christian Heimes180510d2008-03-03 19:15:45 +0000979class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000980
Christian Heimes180510d2008-03-03 19:15:45 +0000981 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000982
Christian Heimes180510d2008-03-03 19:15:45 +0000983 def setUp(self):
984 """Create a dict to remember potentially destroyed objects."""
985 BaseTest.setUp(self)
986 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000987
Christian Heimes180510d2008-03-03 19:15:45 +0000988 def _watch_for_survival(self, *args):
989 """Watch the given objects for survival, by creating weakrefs to
990 them."""
991 for obj in args:
992 key = id(obj), repr(obj)
993 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000994
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000995 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +0000996 """Assert that all objects watched for survival have survived."""
997 # Trigger cycle breaking.
998 gc.collect()
999 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001000 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001001 if ref() is None:
1002 dead.append(repr_)
1003 if dead:
1004 self.fail("%d objects should have survived "
1005 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001006
Christian Heimes180510d2008-03-03 19:15:45 +00001007 def test_persistent_loggers(self):
1008 # Logger objects are persistent and retain their configuration, even
1009 # if visible references are destroyed.
1010 self.root_logger.setLevel(logging.INFO)
1011 foo = logging.getLogger("foo")
1012 self._watch_for_survival(foo)
1013 foo.setLevel(logging.DEBUG)
1014 self.root_logger.debug(self.next_message())
1015 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001016 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001017 ('foo', 'DEBUG', '2'),
1018 ])
1019 del foo
1020 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001021 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001022 # foo has retained its settings.
1023 bar = logging.getLogger("foo")
1024 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001025 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001026 ('foo', 'DEBUG', '2'),
1027 ('foo', 'DEBUG', '3'),
1028 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001029
Benjamin Petersonf91df042009-02-13 02:50:59 +00001030
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001031class EncodingTest(BaseTest):
1032 def test_encoding_plain_file(self):
1033 # In Python 2.x, a plain file object is treated as having no encoding.
1034 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001035 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1036 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001037 # the non-ascii data we write to the log.
1038 data = "foo\x80"
1039 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001040 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001041 log.addHandler(handler)
1042 try:
1043 # write non-ascii data to the log.
1044 log.warning(data)
1045 finally:
1046 log.removeHandler(handler)
1047 handler.close()
1048 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001049 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001050 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001051 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001052 finally:
1053 f.close()
1054 finally:
1055 if os.path.isfile(fn):
1056 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001057
Benjamin Petersonf91df042009-02-13 02:50:59 +00001058 def test_encoding_cyrillic_unicode(self):
1059 log = logging.getLogger("test")
1060 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1061 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1062 #Ensure it's written in a Cyrillic encoding
1063 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001064 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001065 stream = io.BytesIO()
1066 writer = writer_class(stream, 'strict')
1067 handler = logging.StreamHandler(writer)
1068 log.addHandler(handler)
1069 try:
1070 log.warning(message)
1071 finally:
1072 log.removeHandler(handler)
1073 handler.close()
1074 # check we wrote exactly those bytes, ignoring trailing \n etc
1075 s = stream.getvalue()
1076 #Compare against what the data should be when encoded in CP-1251
1077 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1078
1079
Georg Brandlf9734072008-12-07 15:30:06 +00001080class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001081
Georg Brandlf9734072008-12-07 15:30:06 +00001082 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001083 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001084 logging.captureWarnings(True)
Brett Cannondf8709d2009-04-01 20:01:47 +00001085 try:
Brett Cannon5b9082a2009-04-05 18:57:32 +00001086 warnings.filterwarnings("always", category=UserWarning)
Brett Cannondf8709d2009-04-01 20:01:47 +00001087 file = io.StringIO()
1088 h = logging.StreamHandler(file)
1089 logger = logging.getLogger("py.warnings")
1090 logger.addHandler(h)
1091 warnings.warn("I'm warning you...")
1092 logger.removeHandler(h)
1093 s = file.getvalue()
1094 h.close()
1095 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001096
Brett Cannondf8709d2009-04-01 20:01:47 +00001097 #See if an explicit file uses the original implementation
1098 file = io.StringIO()
1099 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1100 file, "Dummy line")
1101 s = file.getvalue()
1102 file.close()
1103 self.assertEqual(s,
1104 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1105 finally:
1106 logging.captureWarnings(False)
Georg Brandlf9734072008-12-07 15:30:06 +00001107
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001108
1109def formatFunc(format, datefmt=None):
1110 return logging.Formatter(format, datefmt)
1111
1112def handlerFunc():
1113 return logging.StreamHandler()
1114
1115class CustomHandler(logging.StreamHandler):
1116 pass
1117
1118class ConfigDictTest(BaseTest):
1119
1120 """Reading logging config from a dictionary."""
1121
1122 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1123
1124 # config0 is a standard configuration.
1125 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001126 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001127 'formatters': {
1128 'form1' : {
1129 'format' : '%(levelname)s ++ %(message)s',
1130 },
1131 },
1132 'handlers' : {
1133 'hand1' : {
1134 'class' : 'logging.StreamHandler',
1135 'formatter' : 'form1',
1136 'level' : 'NOTSET',
1137 'stream' : 'ext://sys.stdout',
1138 },
1139 },
1140 'root' : {
1141 'level' : 'WARNING',
1142 'handlers' : ['hand1'],
1143 },
1144 }
1145
1146 # config1 adds a little to the standard configuration.
1147 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001148 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001149 'formatters': {
1150 'form1' : {
1151 'format' : '%(levelname)s ++ %(message)s',
1152 },
1153 },
1154 'handlers' : {
1155 'hand1' : {
1156 'class' : 'logging.StreamHandler',
1157 'formatter' : 'form1',
1158 'level' : 'NOTSET',
1159 'stream' : 'ext://sys.stdout',
1160 },
1161 },
1162 'loggers' : {
1163 'compiler.parser' : {
1164 'level' : 'DEBUG',
1165 'handlers' : ['hand1'],
1166 },
1167 },
1168 'root' : {
1169 'level' : 'WARNING',
1170 },
1171 }
1172
1173 # config2 has a subtle configuration error that should be reported
1174 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001175 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001176 'formatters': {
1177 'form1' : {
1178 'format' : '%(levelname)s ++ %(message)s',
1179 },
1180 },
1181 'handlers' : {
1182 'hand1' : {
1183 'class' : 'logging.StreamHandler',
1184 'formatter' : 'form1',
1185 'level' : 'NOTSET',
1186 'stream' : 'ext://sys.stdbout',
1187 },
1188 },
1189 'loggers' : {
1190 'compiler.parser' : {
1191 'level' : 'DEBUG',
1192 'handlers' : ['hand1'],
1193 },
1194 },
1195 'root' : {
1196 'level' : 'WARNING',
1197 },
1198 }
1199
1200 #As config1 but with a misspelt level on a handler
1201 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001202 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001203 'formatters': {
1204 'form1' : {
1205 'format' : '%(levelname)s ++ %(message)s',
1206 },
1207 },
1208 'handlers' : {
1209 'hand1' : {
1210 'class' : 'logging.StreamHandler',
1211 'formatter' : 'form1',
1212 'level' : 'NTOSET',
1213 'stream' : 'ext://sys.stdout',
1214 },
1215 },
1216 'loggers' : {
1217 'compiler.parser' : {
1218 'level' : 'DEBUG',
1219 'handlers' : ['hand1'],
1220 },
1221 },
1222 'root' : {
1223 'level' : 'WARNING',
1224 },
1225 }
1226
1227
1228 #As config1 but with a misspelt level on a logger
1229 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001230 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001231 'formatters': {
1232 'form1' : {
1233 'format' : '%(levelname)s ++ %(message)s',
1234 },
1235 },
1236 'handlers' : {
1237 'hand1' : {
1238 'class' : 'logging.StreamHandler',
1239 'formatter' : 'form1',
1240 'level' : 'NOTSET',
1241 'stream' : 'ext://sys.stdout',
1242 },
1243 },
1244 'loggers' : {
1245 'compiler.parser' : {
1246 'level' : 'DEBUG',
1247 'handlers' : ['hand1'],
1248 },
1249 },
1250 'root' : {
1251 'level' : 'WRANING',
1252 },
1253 }
1254
1255 # config3 has a less subtle configuration error
1256 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001257 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001258 'formatters': {
1259 'form1' : {
1260 'format' : '%(levelname)s ++ %(message)s',
1261 },
1262 },
1263 'handlers' : {
1264 'hand1' : {
1265 'class' : 'logging.StreamHandler',
1266 'formatter' : 'misspelled_name',
1267 'level' : 'NOTSET',
1268 'stream' : 'ext://sys.stdout',
1269 },
1270 },
1271 'loggers' : {
1272 'compiler.parser' : {
1273 'level' : 'DEBUG',
1274 'handlers' : ['hand1'],
1275 },
1276 },
1277 'root' : {
1278 'level' : 'WARNING',
1279 },
1280 }
1281
1282 # config4 specifies a custom formatter class to be loaded
1283 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001284 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001285 'formatters': {
1286 'form1' : {
1287 '()' : __name__ + '.ExceptionFormatter',
1288 'format' : '%(levelname)s:%(name)s:%(message)s',
1289 },
1290 },
1291 'handlers' : {
1292 'hand1' : {
1293 'class' : 'logging.StreamHandler',
1294 'formatter' : 'form1',
1295 'level' : 'NOTSET',
1296 'stream' : 'ext://sys.stdout',
1297 },
1298 },
1299 'root' : {
1300 'level' : 'NOTSET',
1301 'handlers' : ['hand1'],
1302 },
1303 }
1304
1305 # As config4 but using an actual callable rather than a string
1306 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001307 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001308 'formatters': {
1309 'form1' : {
1310 '()' : ExceptionFormatter,
1311 'format' : '%(levelname)s:%(name)s:%(message)s',
1312 },
1313 'form2' : {
1314 '()' : __name__ + '.formatFunc',
1315 'format' : '%(levelname)s:%(name)s:%(message)s',
1316 },
1317 'form3' : {
1318 '()' : formatFunc,
1319 'format' : '%(levelname)s:%(name)s:%(message)s',
1320 },
1321 },
1322 'handlers' : {
1323 'hand1' : {
1324 'class' : 'logging.StreamHandler',
1325 'formatter' : 'form1',
1326 'level' : 'NOTSET',
1327 'stream' : 'ext://sys.stdout',
1328 },
1329 'hand2' : {
1330 '()' : handlerFunc,
1331 },
1332 },
1333 'root' : {
1334 'level' : 'NOTSET',
1335 'handlers' : ['hand1'],
1336 },
1337 }
1338
1339 # config5 specifies a custom handler class to be loaded
1340 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001341 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001342 'formatters': {
1343 'form1' : {
1344 'format' : '%(levelname)s ++ %(message)s',
1345 },
1346 },
1347 'handlers' : {
1348 'hand1' : {
1349 'class' : __name__ + '.CustomHandler',
1350 'formatter' : 'form1',
1351 'level' : 'NOTSET',
1352 'stream' : 'ext://sys.stdout',
1353 },
1354 },
1355 'loggers' : {
1356 'compiler.parser' : {
1357 'level' : 'DEBUG',
1358 'handlers' : ['hand1'],
1359 },
1360 },
1361 'root' : {
1362 'level' : 'WARNING',
1363 },
1364 }
1365
1366 # config6 specifies a custom handler class to be loaded
1367 # but has bad arguments
1368 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001369 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001370 'formatters': {
1371 'form1' : {
1372 'format' : '%(levelname)s ++ %(message)s',
1373 },
1374 },
1375 'handlers' : {
1376 'hand1' : {
1377 'class' : __name__ + '.CustomHandler',
1378 'formatter' : 'form1',
1379 'level' : 'NOTSET',
1380 'stream' : 'ext://sys.stdout',
1381 '9' : 'invalid parameter name',
1382 },
1383 },
1384 'loggers' : {
1385 'compiler.parser' : {
1386 'level' : 'DEBUG',
1387 'handlers' : ['hand1'],
1388 },
1389 },
1390 'root' : {
1391 'level' : 'WARNING',
1392 },
1393 }
1394
1395 #config 7 does not define compiler.parser but defines compiler.lexer
1396 #so compiler.parser should be disabled after applying it
1397 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001398 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001399 'formatters': {
1400 'form1' : {
1401 'format' : '%(levelname)s ++ %(message)s',
1402 },
1403 },
1404 'handlers' : {
1405 'hand1' : {
1406 'class' : 'logging.StreamHandler',
1407 'formatter' : 'form1',
1408 'level' : 'NOTSET',
1409 'stream' : 'ext://sys.stdout',
1410 },
1411 },
1412 'loggers' : {
1413 'compiler.lexer' : {
1414 'level' : 'DEBUG',
1415 'handlers' : ['hand1'],
1416 },
1417 },
1418 'root' : {
1419 'level' : 'WARNING',
1420 },
1421 }
1422
1423 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001424 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001425 'disable_existing_loggers' : False,
1426 'formatters': {
1427 'form1' : {
1428 'format' : '%(levelname)s ++ %(message)s',
1429 },
1430 },
1431 'handlers' : {
1432 'hand1' : {
1433 'class' : 'logging.StreamHandler',
1434 'formatter' : 'form1',
1435 'level' : 'NOTSET',
1436 'stream' : 'ext://sys.stdout',
1437 },
1438 },
1439 'loggers' : {
1440 'compiler' : {
1441 'level' : 'DEBUG',
1442 'handlers' : ['hand1'],
1443 },
1444 'compiler.lexer' : {
1445 },
1446 },
1447 'root' : {
1448 'level' : 'WARNING',
1449 },
1450 }
1451
1452 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001453 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001454 'formatters': {
1455 'form1' : {
1456 'format' : '%(levelname)s ++ %(message)s',
1457 },
1458 },
1459 'handlers' : {
1460 'hand1' : {
1461 'class' : 'logging.StreamHandler',
1462 'formatter' : 'form1',
1463 'level' : 'WARNING',
1464 'stream' : 'ext://sys.stdout',
1465 },
1466 },
1467 'loggers' : {
1468 'compiler.parser' : {
1469 'level' : 'WARNING',
1470 'handlers' : ['hand1'],
1471 },
1472 },
1473 'root' : {
1474 'level' : 'NOTSET',
1475 },
1476 }
1477
1478 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001479 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001480 'incremental' : True,
1481 'handlers' : {
1482 'hand1' : {
1483 'level' : 'WARNING',
1484 },
1485 },
1486 'loggers' : {
1487 'compiler.parser' : {
1488 'level' : 'INFO',
1489 },
1490 },
1491 }
1492
1493 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001494 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001495 'incremental' : True,
1496 'handlers' : {
1497 'hand1' : {
1498 'level' : 'INFO',
1499 },
1500 },
1501 'loggers' : {
1502 'compiler.parser' : {
1503 'level' : 'INFO',
1504 },
1505 },
1506 }
1507
1508 #As config1 but with a filter added
1509 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001510 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001511 'formatters': {
1512 'form1' : {
1513 'format' : '%(levelname)s ++ %(message)s',
1514 },
1515 },
1516 'filters' : {
1517 'filt1' : {
1518 'name' : 'compiler.parser',
1519 },
1520 },
1521 'handlers' : {
1522 'hand1' : {
1523 'class' : 'logging.StreamHandler',
1524 'formatter' : 'form1',
1525 'level' : 'NOTSET',
1526 'stream' : 'ext://sys.stdout',
1527 'filters' : ['filt1'],
1528 },
1529 },
1530 'loggers' : {
1531 'compiler.parser' : {
1532 'level' : 'DEBUG',
1533 'filters' : ['filt1'],
1534 },
1535 },
1536 'root' : {
1537 'level' : 'WARNING',
1538 'handlers' : ['hand1'],
1539 },
1540 }
1541
1542 #As config1 but using cfg:// references
1543 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001544 'version': 1,
1545 'true_formatters': {
1546 'form1' : {
1547 'format' : '%(levelname)s ++ %(message)s',
1548 },
1549 },
1550 'handler_configs': {
1551 'hand1' : {
1552 'class' : 'logging.StreamHandler',
1553 'formatter' : 'form1',
1554 'level' : 'NOTSET',
1555 'stream' : 'ext://sys.stdout',
1556 },
1557 },
1558 'formatters' : 'cfg://true_formatters',
1559 'handlers' : {
1560 'hand1' : 'cfg://handler_configs[hand1]',
1561 },
1562 'loggers' : {
1563 'compiler.parser' : {
1564 'level' : 'DEBUG',
1565 'handlers' : ['hand1'],
1566 },
1567 },
1568 'root' : {
1569 'level' : 'WARNING',
1570 },
1571 }
1572
1573 #As config11 but missing the version key
1574 config12 = {
1575 'true_formatters': {
1576 'form1' : {
1577 'format' : '%(levelname)s ++ %(message)s',
1578 },
1579 },
1580 'handler_configs': {
1581 'hand1' : {
1582 'class' : 'logging.StreamHandler',
1583 'formatter' : 'form1',
1584 'level' : 'NOTSET',
1585 'stream' : 'ext://sys.stdout',
1586 },
1587 },
1588 'formatters' : 'cfg://true_formatters',
1589 'handlers' : {
1590 'hand1' : 'cfg://handler_configs[hand1]',
1591 },
1592 'loggers' : {
1593 'compiler.parser' : {
1594 'level' : 'DEBUG',
1595 'handlers' : ['hand1'],
1596 },
1597 },
1598 'root' : {
1599 'level' : 'WARNING',
1600 },
1601 }
1602
1603 #As config11 but using an unsupported version
1604 config13 = {
1605 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001606 'true_formatters': {
1607 'form1' : {
1608 'format' : '%(levelname)s ++ %(message)s',
1609 },
1610 },
1611 'handler_configs': {
1612 'hand1' : {
1613 'class' : 'logging.StreamHandler',
1614 'formatter' : 'form1',
1615 'level' : 'NOTSET',
1616 'stream' : 'ext://sys.stdout',
1617 },
1618 },
1619 'formatters' : 'cfg://true_formatters',
1620 'handlers' : {
1621 'hand1' : 'cfg://handler_configs[hand1]',
1622 },
1623 'loggers' : {
1624 'compiler.parser' : {
1625 'level' : 'DEBUG',
1626 'handlers' : ['hand1'],
1627 },
1628 },
1629 'root' : {
1630 'level' : 'WARNING',
1631 },
1632 }
1633
1634 def apply_config(self, conf):
1635 logging.config.dictConfig(conf)
1636
1637 def test_config0_ok(self):
1638 # A simple config which overrides the default settings.
1639 with captured_stdout() as output:
1640 self.apply_config(self.config0)
1641 logger = logging.getLogger()
1642 # Won't output anything
1643 logger.info(self.next_message())
1644 # Outputs a message
1645 logger.error(self.next_message())
1646 self.assert_log_lines([
1647 ('ERROR', '2'),
1648 ], stream=output)
1649 # Original logger output is empty.
1650 self.assert_log_lines([])
1651
1652 def test_config1_ok(self, config=config1):
1653 # A config defining a sub-parser as well.
1654 with captured_stdout() as output:
1655 self.apply_config(config)
1656 logger = logging.getLogger("compiler.parser")
1657 # Both will output a message
1658 logger.info(self.next_message())
1659 logger.error(self.next_message())
1660 self.assert_log_lines([
1661 ('INFO', '1'),
1662 ('ERROR', '2'),
1663 ], stream=output)
1664 # Original logger output is empty.
1665 self.assert_log_lines([])
1666
1667 def test_config2_failure(self):
1668 # A simple config which overrides the default settings.
1669 self.assertRaises(Exception, self.apply_config, self.config2)
1670
1671 def test_config2a_failure(self):
1672 # A simple config which overrides the default settings.
1673 self.assertRaises(Exception, self.apply_config, self.config2a)
1674
1675 def test_config2b_failure(self):
1676 # A simple config which overrides the default settings.
1677 self.assertRaises(Exception, self.apply_config, self.config2b)
1678
1679 def test_config3_failure(self):
1680 # A simple config which overrides the default settings.
1681 self.assertRaises(Exception, self.apply_config, self.config3)
1682
1683 def test_config4_ok(self):
1684 # A config specifying a custom formatter class.
1685 with captured_stdout() as output:
1686 self.apply_config(self.config4)
1687 #logger = logging.getLogger()
1688 try:
1689 raise RuntimeError()
1690 except RuntimeError:
1691 logging.exception("just testing")
1692 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001693 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001694 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1695 # Original logger output is empty
1696 self.assert_log_lines([])
1697
1698 def test_config4a_ok(self):
1699 # A config specifying a custom formatter class.
1700 with captured_stdout() as output:
1701 self.apply_config(self.config4a)
1702 #logger = logging.getLogger()
1703 try:
1704 raise RuntimeError()
1705 except RuntimeError:
1706 logging.exception("just testing")
1707 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001708 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001709 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1710 # Original logger output is empty
1711 self.assert_log_lines([])
1712
1713 def test_config5_ok(self):
1714 self.test_config1_ok(config=self.config5)
1715
1716 def test_config6_failure(self):
1717 self.assertRaises(Exception, self.apply_config, self.config6)
1718
1719 def test_config7_ok(self):
1720 with captured_stdout() as output:
1721 self.apply_config(self.config1)
1722 logger = logging.getLogger("compiler.parser")
1723 # Both will output a message
1724 logger.info(self.next_message())
1725 logger.error(self.next_message())
1726 self.assert_log_lines([
1727 ('INFO', '1'),
1728 ('ERROR', '2'),
1729 ], stream=output)
1730 # Original logger output is empty.
1731 self.assert_log_lines([])
1732 with captured_stdout() as output:
1733 self.apply_config(self.config7)
1734 logger = logging.getLogger("compiler.parser")
1735 self.assertTrue(logger.disabled)
1736 logger = logging.getLogger("compiler.lexer")
1737 # Both will output a message
1738 logger.info(self.next_message())
1739 logger.error(self.next_message())
1740 self.assert_log_lines([
1741 ('INFO', '3'),
1742 ('ERROR', '4'),
1743 ], stream=output)
1744 # Original logger output is empty.
1745 self.assert_log_lines([])
1746
1747 #Same as test_config_7_ok but don't disable old loggers.
1748 def test_config_8_ok(self):
1749 with captured_stdout() as output:
1750 self.apply_config(self.config1)
1751 logger = logging.getLogger("compiler.parser")
1752 # Both will output a message
1753 logger.info(self.next_message())
1754 logger.error(self.next_message())
1755 self.assert_log_lines([
1756 ('INFO', '1'),
1757 ('ERROR', '2'),
1758 ], stream=output)
1759 # Original logger output is empty.
1760 self.assert_log_lines([])
1761 with captured_stdout() as output:
1762 self.apply_config(self.config8)
1763 logger = logging.getLogger("compiler.parser")
1764 self.assertFalse(logger.disabled)
1765 # Both will output a message
1766 logger.info(self.next_message())
1767 logger.error(self.next_message())
1768 logger = logging.getLogger("compiler.lexer")
1769 # Both will output a message
1770 logger.info(self.next_message())
1771 logger.error(self.next_message())
1772 self.assert_log_lines([
1773 ('INFO', '3'),
1774 ('ERROR', '4'),
1775 ('INFO', '5'),
1776 ('ERROR', '6'),
1777 ], stream=output)
1778 # Original logger output is empty.
1779 self.assert_log_lines([])
1780
1781 def test_config_9_ok(self):
1782 with captured_stdout() as output:
1783 self.apply_config(self.config9)
1784 logger = logging.getLogger("compiler.parser")
1785 #Nothing will be output since both handler and logger are set to WARNING
1786 logger.info(self.next_message())
1787 self.assert_log_lines([], stream=output)
1788 self.apply_config(self.config9a)
1789 #Nothing will be output since both handler is still set to WARNING
1790 logger.info(self.next_message())
1791 self.assert_log_lines([], stream=output)
1792 self.apply_config(self.config9b)
1793 #Message should now be output
1794 logger.info(self.next_message())
1795 self.assert_log_lines([
1796 ('INFO', '3'),
1797 ], stream=output)
1798
1799 def test_config_10_ok(self):
1800 with captured_stdout() as output:
1801 self.apply_config(self.config10)
1802 logger = logging.getLogger("compiler.parser")
1803 logger.warning(self.next_message())
1804 logger = logging.getLogger('compiler')
1805 #Not output, because filtered
1806 logger.warning(self.next_message())
1807 logger = logging.getLogger('compiler.lexer')
1808 #Not output, because filtered
1809 logger.warning(self.next_message())
1810 logger = logging.getLogger("compiler.parser.codegen")
1811 #Output, as not filtered
1812 logger.error(self.next_message())
1813 self.assert_log_lines([
1814 ('WARNING', '1'),
1815 ('ERROR', '4'),
1816 ], stream=output)
1817
1818 def test_config11_ok(self):
1819 self.test_config1_ok(self.config11)
1820
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001821 def test_config12_failure(self):
1822 self.assertRaises(Exception, self.apply_config, self.config12)
1823
1824 def test_config13_failure(self):
1825 self.assertRaises(Exception, self.apply_config, self.config13)
1826
Victor Stinner45df8202010-04-28 22:31:17 +00001827 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001828 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001829 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00001830 # Ask for a randomly assigned port (by using port 0)
1831 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001832 t.start()
1833 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00001834 # Now get the port allocated
1835 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001836 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001837 try:
1838 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1839 sock.settimeout(2.0)
1840 sock.connect(('localhost', port))
1841
1842 slen = struct.pack('>L', len(text))
1843 s = slen + text
1844 sentsofar = 0
1845 left = len(s)
1846 while left > 0:
1847 sent = sock.send(s[sentsofar:])
1848 sentsofar += sent
1849 left -= sent
1850 sock.close()
1851 finally:
1852 t.ready.wait(2.0)
1853 logging.config.stopListening()
1854 t.join(2.0)
1855
1856 def test_listen_config_10_ok(self):
1857 with captured_stdout() as output:
1858 self.setup_via_listener(json.dumps(self.config10))
1859 logger = logging.getLogger("compiler.parser")
1860 logger.warning(self.next_message())
1861 logger = logging.getLogger('compiler')
1862 #Not output, because filtered
1863 logger.warning(self.next_message())
1864 logger = logging.getLogger('compiler.lexer')
1865 #Not output, because filtered
1866 logger.warning(self.next_message())
1867 logger = logging.getLogger("compiler.parser.codegen")
1868 #Output, as not filtered
1869 logger.error(self.next_message())
1870 self.assert_log_lines([
1871 ('WARNING', '1'),
1872 ('ERROR', '4'),
1873 ], stream=output)
1874
1875 def test_listen_config_1_ok(self):
1876 with captured_stdout() as output:
1877 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
1878 logger = logging.getLogger("compiler.parser")
1879 # Both will output a message
1880 logger.info(self.next_message())
1881 logger.error(self.next_message())
1882 self.assert_log_lines([
1883 ('INFO', '1'),
1884 ('ERROR', '2'),
1885 ], stream=output)
1886 # Original logger output is empty.
1887 self.assert_log_lines([])
1888
1889
1890class ManagerTest(BaseTest):
1891 def test_manager_loggerclass(self):
1892 logged = []
1893
1894 class MyLogger(logging.Logger):
1895 def _log(self, level, msg, args, exc_info=None, extra=None):
1896 logged.append(msg)
1897
1898 man = logging.Manager(None)
1899 self.assertRaises(TypeError, man.setLoggerClass, int)
1900 man.setLoggerClass(MyLogger)
1901 logger = man.getLogger('test')
1902 logger.warning('should appear in logged')
1903 logging.warning('should not appear in logged')
1904
1905 self.assertEqual(logged, ['should appear in logged'])
1906
1907
Benjamin Peterson22005fc2010-04-11 16:25:06 +00001908class ChildLoggerTest(BaseTest):
1909 def test_child_loggers(self):
1910 r = logging.getLogger()
1911 l1 = logging.getLogger('abc')
1912 l2 = logging.getLogger('def.ghi')
1913 c1 = r.getChild('xyz')
1914 c2 = r.getChild('uvw.xyz')
1915 self.assertTrue(c1 is logging.getLogger('xyz'))
1916 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
1917 c1 = l1.getChild('def')
1918 c2 = c1.getChild('ghi')
1919 c3 = l1.getChild('def.ghi')
1920 self.assertTrue(c1 is logging.getLogger('abc.def'))
1921 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
1922 self.assertTrue(c2 is c3)
1923
1924
Vinay Sajip6fac8172010-10-19 20:44:14 +00001925class DerivedLogRecord(logging.LogRecord):
1926 pass
1927
Vinay Sajip61561522010-12-03 11:50:38 +00001928class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00001929
1930 def setUp(self):
1931 class CheckingFilter(logging.Filter):
1932 def __init__(self, cls):
1933 self.cls = cls
1934
1935 def filter(self, record):
1936 t = type(record)
1937 if t is not self.cls:
1938 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
1939 self.cls)
1940 raise TypeError(msg)
1941 return True
1942
1943 BaseTest.setUp(self)
1944 self.filter = CheckingFilter(DerivedLogRecord)
1945 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00001946 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00001947
1948 def tearDown(self):
1949 self.root_logger.removeFilter(self.filter)
1950 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00001951 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00001952
1953 def test_logrecord_class(self):
1954 self.assertRaises(TypeError, self.root_logger.warning,
1955 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00001956 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00001957 self.root_logger.error(self.next_message())
1958 self.assert_log_lines([
1959 ('root', 'ERROR', '2'),
1960 ])
1961
1962
Vinay Sajip8552d1f2010-09-14 09:34:09 +00001963class QueueHandlerTest(BaseTest):
1964 # Do not bother with a logger name group.
1965 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
1966
1967 def setUp(self):
1968 BaseTest.setUp(self)
1969 self.queue = queue.Queue(-1)
1970 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
1971 self.que_logger = logging.getLogger('que')
1972 self.que_logger.propagate = False
1973 self.que_logger.setLevel(logging.WARNING)
1974 self.que_logger.addHandler(self.que_hdlr)
1975
1976 def tearDown(self):
1977 self.que_hdlr.close()
1978 BaseTest.tearDown(self)
1979
1980 def test_queue_handler(self):
1981 self.que_logger.debug(self.next_message())
1982 self.assertRaises(queue.Empty, self.queue.get_nowait)
1983 self.que_logger.info(self.next_message())
1984 self.assertRaises(queue.Empty, self.queue.get_nowait)
1985 msg = self.next_message()
1986 self.que_logger.warning(msg)
1987 data = self.queue.get_nowait()
1988 self.assertTrue(isinstance(data, logging.LogRecord))
1989 self.assertEqual(data.name, self.que_logger.name)
1990 self.assertEqual((data.msg, data.args), (msg, None))
1991
Vinay Sajipa39c5712010-10-25 13:57:39 +00001992class FormatterTest(unittest.TestCase):
1993 def setUp(self):
1994 self.common = {
1995 'name': 'formatter.test',
1996 'level': logging.DEBUG,
1997 'pathname': os.path.join('path', 'to', 'dummy.ext'),
1998 'lineno': 42,
1999 'exc_info': None,
2000 'func': None,
2001 'msg': 'Message with %d %s',
2002 'args': (2, 'placeholders'),
2003 }
2004 self.variants = {
2005 }
2006
2007 def get_record(self, name=None):
2008 result = dict(self.common)
2009 if name is not None:
2010 result.update(self.variants[name])
2011 return logging.makeLogRecord(result)
2012
2013 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002014 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002015 r = self.get_record()
2016 f = logging.Formatter('${%(message)s}')
2017 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2018 f = logging.Formatter('%(random)s')
2019 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002020 self.assertFalse(f.usesTime())
2021 f = logging.Formatter('%(asctime)s')
2022 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002023 f = logging.Formatter('%(asctime)-15s')
2024 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002025 f = logging.Formatter('asctime')
2026 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002027
2028 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002029 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002030 r = self.get_record()
2031 f = logging.Formatter('$%{message}%$', style='{')
2032 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2033 f = logging.Formatter('{random}', style='{')
2034 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002035 self.assertFalse(f.usesTime())
2036 f = logging.Formatter('{asctime}', style='{')
2037 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002038 f = logging.Formatter('{asctime!s:15}', style='{')
2039 self.assertTrue(f.usesTime())
2040 f = logging.Formatter('{asctime:15}', style='{')
2041 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002042 f = logging.Formatter('asctime', style='{')
2043 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002044
2045 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002046 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002047 r = self.get_record()
2048 f = logging.Formatter('$message', style='$')
2049 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2050 f = logging.Formatter('$$%${message}%$$', style='$')
2051 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2052 f = logging.Formatter('${random}', style='$')
2053 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002054 self.assertFalse(f.usesTime())
2055 f = logging.Formatter('${asctime}', style='$')
2056 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002057 f = logging.Formatter('${asctime', style='$')
2058 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002059 f = logging.Formatter('$asctime', style='$')
2060 self.assertTrue(f.usesTime())
2061 f = logging.Formatter('asctime', style='$')
2062 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002063
Vinay Sajip5a27d402010-12-10 11:42:57 +00002064class LastResortTest(BaseTest):
2065 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002066 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002067 root = self.root_logger
2068 root.removeHandler(self.root_hdlr)
2069 old_stderr = sys.stderr
2070 old_lastresort = logging.lastResort
2071 old_raise_exceptions = logging.raiseExceptions
2072 try:
2073 sys.stderr = sio = io.StringIO()
2074 root.warning('This is your final chance!')
2075 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2076 #No handlers and no last resort, so 'No handlers' message
2077 logging.lastResort = None
2078 sys.stderr = sio = io.StringIO()
2079 root.warning('This is your final chance!')
2080 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2081 # 'No handlers' message only printed once
2082 sys.stderr = sio = io.StringIO()
2083 root.warning('This is your final chance!')
2084 self.assertEqual(sio.getvalue(), '')
2085 root.manager.emittedNoHandlerWarning = False
2086 #If raiseExceptions is False, no message is printed
2087 logging.raiseExceptions = False
2088 sys.stderr = sio = io.StringIO()
2089 root.warning('This is your final chance!')
2090 self.assertEqual(sio.getvalue(), '')
2091 finally:
2092 sys.stderr = old_stderr
2093 root.addHandler(self.root_hdlr)
2094 logging.lastResort = old_lastresort
2095 logging.raiseExceptions = old_raise_exceptions
2096
2097
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002098class BaseFileTest(BaseTest):
2099 "Base class for handler tests that write log files"
2100
2101 def setUp(self):
2102 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002103 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2104 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002105 self.rmfiles = []
2106
2107 def tearDown(self):
2108 for fn in self.rmfiles:
2109 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002110 if os.path.exists(self.fn):
2111 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002112 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002113
2114 def assertLogFile(self, filename):
2115 "Assert a log file is there and register it for deletion"
2116 self.assertTrue(os.path.exists(filename),
2117 msg="Log file %r does not exist")
2118 self.rmfiles.append(filename)
2119
2120
2121class RotatingFileHandlerTest(BaseFileTest):
2122 def next_rec(self):
2123 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2124 self.next_message(), None, None, None)
2125
2126 def test_should_not_rollover(self):
2127 # If maxbytes is zero rollover never occurs
2128 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2129 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002130 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002131
2132 def test_should_rollover(self):
2133 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2134 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002135 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002136
2137 def test_file_created(self):
2138 # checks that the file is created and assumes it was created
2139 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002140 rh = logging.handlers.RotatingFileHandler(self.fn)
2141 rh.emit(self.next_rec())
2142 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002143 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002144
2145 def test_rollover_filenames(self):
2146 rh = logging.handlers.RotatingFileHandler(
2147 self.fn, backupCount=2, maxBytes=1)
2148 rh.emit(self.next_rec())
2149 self.assertLogFile(self.fn)
2150 rh.emit(self.next_rec())
2151 self.assertLogFile(self.fn + ".1")
2152 rh.emit(self.next_rec())
2153 self.assertLogFile(self.fn + ".2")
2154 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002155 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002156
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002157class TimedRotatingFileHandlerTest(BaseFileTest):
2158 # test methods added below
2159 pass
2160
2161def secs(**kw):
2162 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2163
2164for when, exp in (('S', 1),
2165 ('M', 60),
2166 ('H', 60 * 60),
2167 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002168 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002169 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002170 ('W0', secs(days=4, hours=24)),
2171 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002172 def test_compute_rollover(self, when=when, exp=exp):
2173 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002174 self.fn, when=when, interval=1, backupCount=0, utc=True)
2175 currentTime = 0.0
2176 actual = rh.computeRollover(currentTime)
2177 if exp != actual:
2178 # Failures occur on some systems for MIDNIGHT and W0.
2179 # Print detailed calculation for MIDNIGHT so we can try to see
2180 # what's going on
2181 import time
2182 if when == 'MIDNIGHT':
2183 try:
2184 if rh.utc:
2185 t = time.gmtime(currentTime)
2186 else:
2187 t = time.localtime(currentTime)
2188 currentHour = t[3]
2189 currentMinute = t[4]
2190 currentSecond = t[5]
2191 # r is the number of seconds left between now and midnight
2192 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
2193 currentMinute) * 60 +
2194 currentSecond)
2195 result = currentTime + r
2196 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
2197 print('currentHour: %s' % currentHour, file=sys.stderr)
2198 print('currentMinute: %s' % currentMinute, file=sys.stderr)
2199 print('currentSecond: %s' % currentSecond, file=sys.stderr)
2200 print('r: %s' % r, file=sys.stderr)
2201 print('result: %s' % result, file=sys.stderr)
2202 except Exception:
2203 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
2204 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00002205 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002206 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
2207
Christian Heimes180510d2008-03-03 19:15:45 +00002208# Set the locale to the platform-dependent default. I have no idea
2209# why the test does this, but in any case we save the current locale
2210# first and restore it at the end.
2211@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00002212def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00002213 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002214 CustomLevelsAndFiltersTest, MemoryHandlerTest,
2215 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002216 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajipa39c5712010-10-25 13:57:39 +00002217 FormatterTest,
Vinay Sajip61561522010-12-03 11:50:38 +00002218 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipbc85d842010-09-17 23:35:29 +00002219 RotatingFileHandlerTest,
Vinay Sajip5a27d402010-12-10 11:42:57 +00002220 LastResortTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00002221 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00002222 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00002223
Christian Heimes180510d2008-03-03 19:15:45 +00002224if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002225 test_main()