blob: b29d4005eebbca8944b9a90a8bfbd022eb5ba4d0 [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
614 # config2 has a subtle configuration error that should be reported
615 config2 = config1.replace("sys.stdout", "sys.stbout")
616
617 # config3 has a less subtle configuration error
618 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
619
620 # config4 specifies a custom formatter class to be loaded
621 config4 = """
622 [loggers]
623 keys=root
624
625 [handlers]
626 keys=hand1
627
628 [formatters]
629 keys=form1
630
631 [logger_root]
632 level=NOTSET
633 handlers=hand1
634
635 [handler_hand1]
636 class=StreamHandler
637 level=NOTSET
638 formatter=form1
639 args=(sys.stdout,)
640
641 [formatter_form1]
642 class=""" + __name__ + """.ExceptionFormatter
643 format=%(levelname)s:%(name)s:%(message)s
644 datefmt=
645 """
646
Georg Brandl3dbca812008-07-23 16:10:53 +0000647 # config5 specifies a custom handler class to be loaded
648 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
649
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000650 # config6 uses ', ' delimiters in the handlers and formatters sections
651 config6 = """
652 [loggers]
653 keys=root,parser
654
655 [handlers]
656 keys=hand1, hand2
657
658 [formatters]
659 keys=form1, form2
660
661 [logger_root]
662 level=WARNING
663 handlers=
664
665 [logger_parser]
666 level=DEBUG
667 handlers=hand1
668 propagate=1
669 qualname=compiler.parser
670
671 [handler_hand1]
672 class=StreamHandler
673 level=NOTSET
674 formatter=form1
675 args=(sys.stdout,)
676
677 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000678 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000679 level=NOTSET
680 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000681 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000682
683 [formatter_form1]
684 format=%(levelname)s ++ %(message)s
685 datefmt=
686
687 [formatter_form2]
688 format=%(message)s
689 datefmt=
690 """
691
Christian Heimes180510d2008-03-03 19:15:45 +0000692 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000693 file = io.StringIO(textwrap.dedent(conf))
694 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000695
696 def test_config0_ok(self):
697 # A simple config file which overrides the default settings.
698 with captured_stdout() as output:
699 self.apply_config(self.config0)
700 logger = logging.getLogger()
701 # Won't output anything
702 logger.info(self.next_message())
703 # Outputs a message
704 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000705 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000706 ('ERROR', '2'),
707 ], stream=output)
708 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000709 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000710
Georg Brandl3dbca812008-07-23 16:10:53 +0000711 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000712 # A config file defining a sub-parser as well.
713 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000714 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000715 logger = logging.getLogger("compiler.parser")
716 # Both will output a message
717 logger.info(self.next_message())
718 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000719 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000720 ('INFO', '1'),
721 ('ERROR', '2'),
722 ], stream=output)
723 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000724 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000725
726 def test_config2_failure(self):
727 # A simple config file which overrides the default settings.
728 self.assertRaises(Exception, self.apply_config, self.config2)
729
730 def test_config3_failure(self):
731 # A simple config file which overrides the default settings.
732 self.assertRaises(Exception, self.apply_config, self.config3)
733
734 def test_config4_ok(self):
735 # A config file specifying a custom formatter class.
736 with captured_stdout() as output:
737 self.apply_config(self.config4)
738 logger = logging.getLogger()
739 try:
740 raise RuntimeError()
741 except RuntimeError:
742 logging.exception("just testing")
743 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000744 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000745 "ERROR:root:just testing\nGot a [RuntimeError]\n")
746 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000747 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000748
Georg Brandl3dbca812008-07-23 16:10:53 +0000749 def test_config5_ok(self):
750 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000751
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000752 def test_config6_ok(self):
753 self.test_config1_ok(config=self.config6)
754
Christian Heimes180510d2008-03-03 19:15:45 +0000755class LogRecordStreamHandler(StreamRequestHandler):
756
757 """Handler for a streaming logging request. It saves the log message in the
758 TCP server's 'log_output' attribute."""
759
760 TCP_LOG_END = "!!!END!!!"
761
762 def handle(self):
763 """Handle multiple requests - each expected to be of 4-byte length,
764 followed by the LogRecord in pickle format. Logs the record
765 according to whatever policy is configured locally."""
766 while True:
767 chunk = self.connection.recv(4)
768 if len(chunk) < 4:
769 break
770 slen = struct.unpack(">L", chunk)[0]
771 chunk = self.connection.recv(slen)
772 while len(chunk) < slen:
773 chunk = chunk + self.connection.recv(slen - len(chunk))
774 obj = self.unpickle(chunk)
775 record = logging.makeLogRecord(obj)
776 self.handle_log_record(record)
777
778 def unpickle(self, data):
779 return pickle.loads(data)
780
781 def handle_log_record(self, record):
782 # If the end-of-messages sentinel is seen, tell the server to
783 # terminate.
784 if self.TCP_LOG_END in record.msg:
785 self.server.abort = 1
786 return
787 self.server.log_output += record.msg + "\n"
788
Guido van Rossum376e6362003-04-25 14:22:00 +0000789
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000790class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +0000791
792 """A simple-minded TCP socket-based logging receiver suitable for test
793 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000794
795 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +0000796 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000797
798 def __init__(self, host='localhost',
799 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
800 handler=LogRecordStreamHandler):
801 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +0000802 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +0000803 self.timeout = 0.1
804 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000805
806 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000807 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000808 rd, wr, ex = select.select([self.socket.fileno()], [], [],
809 self.timeout)
810 if rd:
811 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +0000812 # Notify the main thread that we're about to exit
813 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000814 # close the listen socket
815 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000816
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000817
Victor Stinner45df8202010-04-28 22:31:17 +0000818@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +0000819class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000820
Christian Heimes180510d2008-03-03 19:15:45 +0000821 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000822
Christian Heimes180510d2008-03-03 19:15:45 +0000823 def setUp(self):
824 """Set up a TCP server to receive log messages, and a SocketHandler
825 pointing to that server's address and port."""
826 BaseTest.setUp(self)
827 self.tcpserver = LogRecordSocketReceiver(port=0)
828 self.port = self.tcpserver.socket.getsockname()[1]
829 self.threads = [
830 threading.Thread(target=self.tcpserver.serve_until_stopped)]
831 for thread in self.threads:
832 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000833
Christian Heimes180510d2008-03-03 19:15:45 +0000834 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
835 self.sock_hdlr.setFormatter(self.root_formatter)
836 self.root_logger.removeHandler(self.root_logger.handlers[0])
837 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000838
Christian Heimes180510d2008-03-03 19:15:45 +0000839 def tearDown(self):
840 """Shutdown the TCP server."""
841 try:
842 self.tcpserver.abort = True
843 del self.tcpserver
844 self.root_logger.removeHandler(self.sock_hdlr)
845 self.sock_hdlr.close()
846 for thread in self.threads:
847 thread.join(2.0)
848 finally:
849 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000850
Christian Heimes180510d2008-03-03 19:15:45 +0000851 def get_output(self):
852 """Get the log output as received by the TCP server."""
853 # Signal the TCP receiver and wait for it to terminate.
854 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
855 self.tcpserver.finished.wait(2.0)
856 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000857
Christian Heimes180510d2008-03-03 19:15:45 +0000858 def test_output(self):
859 # The log message sent to the SocketHandler is properly received.
860 logger = logging.getLogger("tcp")
861 logger.error("spam")
862 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000863 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000864
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000865
Christian Heimes180510d2008-03-03 19:15:45 +0000866class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000867
Christian Heimes180510d2008-03-03 19:15:45 +0000868 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000869
Christian Heimes180510d2008-03-03 19:15:45 +0000870 def setUp(self):
871 """Create a dict to remember potentially destroyed objects."""
872 BaseTest.setUp(self)
873 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000874
Christian Heimes180510d2008-03-03 19:15:45 +0000875 def _watch_for_survival(self, *args):
876 """Watch the given objects for survival, by creating weakrefs to
877 them."""
878 for obj in args:
879 key = id(obj), repr(obj)
880 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000881
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000882 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +0000883 """Assert that all objects watched for survival have survived."""
884 # Trigger cycle breaking.
885 gc.collect()
886 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000887 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000888 if ref() is None:
889 dead.append(repr_)
890 if dead:
891 self.fail("%d objects should have survived "
892 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000893
Christian Heimes180510d2008-03-03 19:15:45 +0000894 def test_persistent_loggers(self):
895 # Logger objects are persistent and retain their configuration, even
896 # if visible references are destroyed.
897 self.root_logger.setLevel(logging.INFO)
898 foo = logging.getLogger("foo")
899 self._watch_for_survival(foo)
900 foo.setLevel(logging.DEBUG)
901 self.root_logger.debug(self.next_message())
902 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000903 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000904 ('foo', 'DEBUG', '2'),
905 ])
906 del foo
907 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000908 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +0000909 # foo has retained its settings.
910 bar = logging.getLogger("foo")
911 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000912 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000913 ('foo', 'DEBUG', '2'),
914 ('foo', 'DEBUG', '3'),
915 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000916
Benjamin Petersonf91df042009-02-13 02:50:59 +0000917
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000918class EncodingTest(BaseTest):
919 def test_encoding_plain_file(self):
920 # In Python 2.x, a plain file object is treated as having no encoding.
921 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +0000922 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
923 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000924 # the non-ascii data we write to the log.
925 data = "foo\x80"
926 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000927 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000928 log.addHandler(handler)
929 try:
930 # write non-ascii data to the log.
931 log.warning(data)
932 finally:
933 log.removeHandler(handler)
934 handler.close()
935 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000936 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000937 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000938 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000939 finally:
940 f.close()
941 finally:
942 if os.path.isfile(fn):
943 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000944
Benjamin Petersonf91df042009-02-13 02:50:59 +0000945 def test_encoding_cyrillic_unicode(self):
946 log = logging.getLogger("test")
947 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
948 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
949 #Ensure it's written in a Cyrillic encoding
950 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000951 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +0000952 stream = io.BytesIO()
953 writer = writer_class(stream, 'strict')
954 handler = logging.StreamHandler(writer)
955 log.addHandler(handler)
956 try:
957 log.warning(message)
958 finally:
959 log.removeHandler(handler)
960 handler.close()
961 # check we wrote exactly those bytes, ignoring trailing \n etc
962 s = stream.getvalue()
963 #Compare against what the data should be when encoded in CP-1251
964 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
965
966
Georg Brandlf9734072008-12-07 15:30:06 +0000967class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +0000968
Georg Brandlf9734072008-12-07 15:30:06 +0000969 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +0000970 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +0000971 logging.captureWarnings(True)
Brett Cannondf8709d2009-04-01 20:01:47 +0000972 try:
Brett Cannon5b9082a2009-04-05 18:57:32 +0000973 warnings.filterwarnings("always", category=UserWarning)
Brett Cannondf8709d2009-04-01 20:01:47 +0000974 file = io.StringIO()
975 h = logging.StreamHandler(file)
976 logger = logging.getLogger("py.warnings")
977 logger.addHandler(h)
978 warnings.warn("I'm warning you...")
979 logger.removeHandler(h)
980 s = file.getvalue()
981 h.close()
982 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +0000983
Brett Cannondf8709d2009-04-01 20:01:47 +0000984 #See if an explicit file uses the original implementation
985 file = io.StringIO()
986 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
987 file, "Dummy line")
988 s = file.getvalue()
989 file.close()
990 self.assertEqual(s,
991 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
992 finally:
993 logging.captureWarnings(False)
Georg Brandlf9734072008-12-07 15:30:06 +0000994
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000995
996def formatFunc(format, datefmt=None):
997 return logging.Formatter(format, datefmt)
998
999def handlerFunc():
1000 return logging.StreamHandler()
1001
1002class CustomHandler(logging.StreamHandler):
1003 pass
1004
1005class ConfigDictTest(BaseTest):
1006
1007 """Reading logging config from a dictionary."""
1008
1009 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1010
1011 # config0 is a standard configuration.
1012 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001013 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001014 'formatters': {
1015 'form1' : {
1016 'format' : '%(levelname)s ++ %(message)s',
1017 },
1018 },
1019 'handlers' : {
1020 'hand1' : {
1021 'class' : 'logging.StreamHandler',
1022 'formatter' : 'form1',
1023 'level' : 'NOTSET',
1024 'stream' : 'ext://sys.stdout',
1025 },
1026 },
1027 'root' : {
1028 'level' : 'WARNING',
1029 'handlers' : ['hand1'],
1030 },
1031 }
1032
1033 # config1 adds a little to the standard configuration.
1034 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001035 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001036 'formatters': {
1037 'form1' : {
1038 'format' : '%(levelname)s ++ %(message)s',
1039 },
1040 },
1041 'handlers' : {
1042 'hand1' : {
1043 'class' : 'logging.StreamHandler',
1044 'formatter' : 'form1',
1045 'level' : 'NOTSET',
1046 'stream' : 'ext://sys.stdout',
1047 },
1048 },
1049 'loggers' : {
1050 'compiler.parser' : {
1051 'level' : 'DEBUG',
1052 'handlers' : ['hand1'],
1053 },
1054 },
1055 'root' : {
1056 'level' : 'WARNING',
1057 },
1058 }
1059
1060 # config2 has a subtle configuration error that should be reported
1061 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001062 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001063 'formatters': {
1064 'form1' : {
1065 'format' : '%(levelname)s ++ %(message)s',
1066 },
1067 },
1068 'handlers' : {
1069 'hand1' : {
1070 'class' : 'logging.StreamHandler',
1071 'formatter' : 'form1',
1072 'level' : 'NOTSET',
1073 'stream' : 'ext://sys.stdbout',
1074 },
1075 },
1076 'loggers' : {
1077 'compiler.parser' : {
1078 'level' : 'DEBUG',
1079 'handlers' : ['hand1'],
1080 },
1081 },
1082 'root' : {
1083 'level' : 'WARNING',
1084 },
1085 }
1086
1087 #As config1 but with a misspelt level on a handler
1088 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001089 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001090 'formatters': {
1091 'form1' : {
1092 'format' : '%(levelname)s ++ %(message)s',
1093 },
1094 },
1095 'handlers' : {
1096 'hand1' : {
1097 'class' : 'logging.StreamHandler',
1098 'formatter' : 'form1',
1099 'level' : 'NTOSET',
1100 'stream' : 'ext://sys.stdout',
1101 },
1102 },
1103 'loggers' : {
1104 'compiler.parser' : {
1105 'level' : 'DEBUG',
1106 'handlers' : ['hand1'],
1107 },
1108 },
1109 'root' : {
1110 'level' : 'WARNING',
1111 },
1112 }
1113
1114
1115 #As config1 but with a misspelt level on a logger
1116 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001117 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001118 'formatters': {
1119 'form1' : {
1120 'format' : '%(levelname)s ++ %(message)s',
1121 },
1122 },
1123 'handlers' : {
1124 'hand1' : {
1125 'class' : 'logging.StreamHandler',
1126 'formatter' : 'form1',
1127 'level' : 'NOTSET',
1128 'stream' : 'ext://sys.stdout',
1129 },
1130 },
1131 'loggers' : {
1132 'compiler.parser' : {
1133 'level' : 'DEBUG',
1134 'handlers' : ['hand1'],
1135 },
1136 },
1137 'root' : {
1138 'level' : 'WRANING',
1139 },
1140 }
1141
1142 # config3 has a less subtle configuration error
1143 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001144 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001145 'formatters': {
1146 'form1' : {
1147 'format' : '%(levelname)s ++ %(message)s',
1148 },
1149 },
1150 'handlers' : {
1151 'hand1' : {
1152 'class' : 'logging.StreamHandler',
1153 'formatter' : 'misspelled_name',
1154 'level' : 'NOTSET',
1155 'stream' : 'ext://sys.stdout',
1156 },
1157 },
1158 'loggers' : {
1159 'compiler.parser' : {
1160 'level' : 'DEBUG',
1161 'handlers' : ['hand1'],
1162 },
1163 },
1164 'root' : {
1165 'level' : 'WARNING',
1166 },
1167 }
1168
1169 # config4 specifies a custom formatter class to be loaded
1170 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001171 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001172 'formatters': {
1173 'form1' : {
1174 '()' : __name__ + '.ExceptionFormatter',
1175 'format' : '%(levelname)s:%(name)s:%(message)s',
1176 },
1177 },
1178 'handlers' : {
1179 'hand1' : {
1180 'class' : 'logging.StreamHandler',
1181 'formatter' : 'form1',
1182 'level' : 'NOTSET',
1183 'stream' : 'ext://sys.stdout',
1184 },
1185 },
1186 'root' : {
1187 'level' : 'NOTSET',
1188 'handlers' : ['hand1'],
1189 },
1190 }
1191
1192 # As config4 but using an actual callable rather than a string
1193 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001194 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001195 'formatters': {
1196 'form1' : {
1197 '()' : ExceptionFormatter,
1198 'format' : '%(levelname)s:%(name)s:%(message)s',
1199 },
1200 'form2' : {
1201 '()' : __name__ + '.formatFunc',
1202 'format' : '%(levelname)s:%(name)s:%(message)s',
1203 },
1204 'form3' : {
1205 '()' : formatFunc,
1206 'format' : '%(levelname)s:%(name)s:%(message)s',
1207 },
1208 },
1209 'handlers' : {
1210 'hand1' : {
1211 'class' : 'logging.StreamHandler',
1212 'formatter' : 'form1',
1213 'level' : 'NOTSET',
1214 'stream' : 'ext://sys.stdout',
1215 },
1216 'hand2' : {
1217 '()' : handlerFunc,
1218 },
1219 },
1220 'root' : {
1221 'level' : 'NOTSET',
1222 'handlers' : ['hand1'],
1223 },
1224 }
1225
1226 # config5 specifies a custom handler class to be loaded
1227 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001228 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001229 'formatters': {
1230 'form1' : {
1231 'format' : '%(levelname)s ++ %(message)s',
1232 },
1233 },
1234 'handlers' : {
1235 'hand1' : {
1236 'class' : __name__ + '.CustomHandler',
1237 'formatter' : 'form1',
1238 'level' : 'NOTSET',
1239 'stream' : 'ext://sys.stdout',
1240 },
1241 },
1242 'loggers' : {
1243 'compiler.parser' : {
1244 'level' : 'DEBUG',
1245 'handlers' : ['hand1'],
1246 },
1247 },
1248 'root' : {
1249 'level' : 'WARNING',
1250 },
1251 }
1252
1253 # config6 specifies a custom handler class to be loaded
1254 # but has bad arguments
1255 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001256 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001257 'formatters': {
1258 'form1' : {
1259 'format' : '%(levelname)s ++ %(message)s',
1260 },
1261 },
1262 'handlers' : {
1263 'hand1' : {
1264 'class' : __name__ + '.CustomHandler',
1265 'formatter' : 'form1',
1266 'level' : 'NOTSET',
1267 'stream' : 'ext://sys.stdout',
1268 '9' : 'invalid parameter name',
1269 },
1270 },
1271 'loggers' : {
1272 'compiler.parser' : {
1273 'level' : 'DEBUG',
1274 'handlers' : ['hand1'],
1275 },
1276 },
1277 'root' : {
1278 'level' : 'WARNING',
1279 },
1280 }
1281
1282 #config 7 does not define compiler.parser but defines compiler.lexer
1283 #so compiler.parser should be disabled after applying it
1284 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001285 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001286 'formatters': {
1287 'form1' : {
1288 'format' : '%(levelname)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 'loggers' : {
1300 'compiler.lexer' : {
1301 'level' : 'DEBUG',
1302 'handlers' : ['hand1'],
1303 },
1304 },
1305 'root' : {
1306 'level' : 'WARNING',
1307 },
1308 }
1309
1310 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001311 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001312 'disable_existing_loggers' : False,
1313 'formatters': {
1314 'form1' : {
1315 'format' : '%(levelname)s ++ %(message)s',
1316 },
1317 },
1318 'handlers' : {
1319 'hand1' : {
1320 'class' : 'logging.StreamHandler',
1321 'formatter' : 'form1',
1322 'level' : 'NOTSET',
1323 'stream' : 'ext://sys.stdout',
1324 },
1325 },
1326 'loggers' : {
1327 'compiler' : {
1328 'level' : 'DEBUG',
1329 'handlers' : ['hand1'],
1330 },
1331 'compiler.lexer' : {
1332 },
1333 },
1334 'root' : {
1335 'level' : 'WARNING',
1336 },
1337 }
1338
1339 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001340 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001341 'formatters': {
1342 'form1' : {
1343 'format' : '%(levelname)s ++ %(message)s',
1344 },
1345 },
1346 'handlers' : {
1347 'hand1' : {
1348 'class' : 'logging.StreamHandler',
1349 'formatter' : 'form1',
1350 'level' : 'WARNING',
1351 'stream' : 'ext://sys.stdout',
1352 },
1353 },
1354 'loggers' : {
1355 'compiler.parser' : {
1356 'level' : 'WARNING',
1357 'handlers' : ['hand1'],
1358 },
1359 },
1360 'root' : {
1361 'level' : 'NOTSET',
1362 },
1363 }
1364
1365 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001366 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001367 'incremental' : True,
1368 'handlers' : {
1369 'hand1' : {
1370 'level' : 'WARNING',
1371 },
1372 },
1373 'loggers' : {
1374 'compiler.parser' : {
1375 'level' : 'INFO',
1376 },
1377 },
1378 }
1379
1380 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001381 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001382 'incremental' : True,
1383 'handlers' : {
1384 'hand1' : {
1385 'level' : 'INFO',
1386 },
1387 },
1388 'loggers' : {
1389 'compiler.parser' : {
1390 'level' : 'INFO',
1391 },
1392 },
1393 }
1394
1395 #As config1 but with a filter added
1396 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001397 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001398 'formatters': {
1399 'form1' : {
1400 'format' : '%(levelname)s ++ %(message)s',
1401 },
1402 },
1403 'filters' : {
1404 'filt1' : {
1405 'name' : 'compiler.parser',
1406 },
1407 },
1408 'handlers' : {
1409 'hand1' : {
1410 'class' : 'logging.StreamHandler',
1411 'formatter' : 'form1',
1412 'level' : 'NOTSET',
1413 'stream' : 'ext://sys.stdout',
1414 'filters' : ['filt1'],
1415 },
1416 },
1417 'loggers' : {
1418 'compiler.parser' : {
1419 'level' : 'DEBUG',
1420 'filters' : ['filt1'],
1421 },
1422 },
1423 'root' : {
1424 'level' : 'WARNING',
1425 'handlers' : ['hand1'],
1426 },
1427 }
1428
1429 #As config1 but using cfg:// references
1430 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001431 'version': 1,
1432 'true_formatters': {
1433 'form1' : {
1434 'format' : '%(levelname)s ++ %(message)s',
1435 },
1436 },
1437 'handler_configs': {
1438 'hand1' : {
1439 'class' : 'logging.StreamHandler',
1440 'formatter' : 'form1',
1441 'level' : 'NOTSET',
1442 'stream' : 'ext://sys.stdout',
1443 },
1444 },
1445 'formatters' : 'cfg://true_formatters',
1446 'handlers' : {
1447 'hand1' : 'cfg://handler_configs[hand1]',
1448 },
1449 'loggers' : {
1450 'compiler.parser' : {
1451 'level' : 'DEBUG',
1452 'handlers' : ['hand1'],
1453 },
1454 },
1455 'root' : {
1456 'level' : 'WARNING',
1457 },
1458 }
1459
1460 #As config11 but missing the version key
1461 config12 = {
1462 'true_formatters': {
1463 'form1' : {
1464 'format' : '%(levelname)s ++ %(message)s',
1465 },
1466 },
1467 'handler_configs': {
1468 'hand1' : {
1469 'class' : 'logging.StreamHandler',
1470 'formatter' : 'form1',
1471 'level' : 'NOTSET',
1472 'stream' : 'ext://sys.stdout',
1473 },
1474 },
1475 'formatters' : 'cfg://true_formatters',
1476 'handlers' : {
1477 'hand1' : 'cfg://handler_configs[hand1]',
1478 },
1479 'loggers' : {
1480 'compiler.parser' : {
1481 'level' : 'DEBUG',
1482 'handlers' : ['hand1'],
1483 },
1484 },
1485 'root' : {
1486 'level' : 'WARNING',
1487 },
1488 }
1489
1490 #As config11 but using an unsupported version
1491 config13 = {
1492 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001493 'true_formatters': {
1494 'form1' : {
1495 'format' : '%(levelname)s ++ %(message)s',
1496 },
1497 },
1498 'handler_configs': {
1499 'hand1' : {
1500 'class' : 'logging.StreamHandler',
1501 'formatter' : 'form1',
1502 'level' : 'NOTSET',
1503 'stream' : 'ext://sys.stdout',
1504 },
1505 },
1506 'formatters' : 'cfg://true_formatters',
1507 'handlers' : {
1508 'hand1' : 'cfg://handler_configs[hand1]',
1509 },
1510 'loggers' : {
1511 'compiler.parser' : {
1512 'level' : 'DEBUG',
1513 'handlers' : ['hand1'],
1514 },
1515 },
1516 'root' : {
1517 'level' : 'WARNING',
1518 },
1519 }
1520
1521 def apply_config(self, conf):
1522 logging.config.dictConfig(conf)
1523
1524 def test_config0_ok(self):
1525 # A simple config which overrides the default settings.
1526 with captured_stdout() as output:
1527 self.apply_config(self.config0)
1528 logger = logging.getLogger()
1529 # Won't output anything
1530 logger.info(self.next_message())
1531 # Outputs a message
1532 logger.error(self.next_message())
1533 self.assert_log_lines([
1534 ('ERROR', '2'),
1535 ], stream=output)
1536 # Original logger output is empty.
1537 self.assert_log_lines([])
1538
1539 def test_config1_ok(self, config=config1):
1540 # A config defining a sub-parser as well.
1541 with captured_stdout() as output:
1542 self.apply_config(config)
1543 logger = logging.getLogger("compiler.parser")
1544 # Both will output a message
1545 logger.info(self.next_message())
1546 logger.error(self.next_message())
1547 self.assert_log_lines([
1548 ('INFO', '1'),
1549 ('ERROR', '2'),
1550 ], stream=output)
1551 # Original logger output is empty.
1552 self.assert_log_lines([])
1553
1554 def test_config2_failure(self):
1555 # A simple config which overrides the default settings.
1556 self.assertRaises(Exception, self.apply_config, self.config2)
1557
1558 def test_config2a_failure(self):
1559 # A simple config which overrides the default settings.
1560 self.assertRaises(Exception, self.apply_config, self.config2a)
1561
1562 def test_config2b_failure(self):
1563 # A simple config which overrides the default settings.
1564 self.assertRaises(Exception, self.apply_config, self.config2b)
1565
1566 def test_config3_failure(self):
1567 # A simple config which overrides the default settings.
1568 self.assertRaises(Exception, self.apply_config, self.config3)
1569
1570 def test_config4_ok(self):
1571 # A config specifying a custom formatter class.
1572 with captured_stdout() as output:
1573 self.apply_config(self.config4)
1574 #logger = logging.getLogger()
1575 try:
1576 raise RuntimeError()
1577 except RuntimeError:
1578 logging.exception("just testing")
1579 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001580 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001581 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1582 # Original logger output is empty
1583 self.assert_log_lines([])
1584
1585 def test_config4a_ok(self):
1586 # A config specifying a custom formatter class.
1587 with captured_stdout() as output:
1588 self.apply_config(self.config4a)
1589 #logger = logging.getLogger()
1590 try:
1591 raise RuntimeError()
1592 except RuntimeError:
1593 logging.exception("just testing")
1594 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001595 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001596 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1597 # Original logger output is empty
1598 self.assert_log_lines([])
1599
1600 def test_config5_ok(self):
1601 self.test_config1_ok(config=self.config5)
1602
1603 def test_config6_failure(self):
1604 self.assertRaises(Exception, self.apply_config, self.config6)
1605
1606 def test_config7_ok(self):
1607 with captured_stdout() as output:
1608 self.apply_config(self.config1)
1609 logger = logging.getLogger("compiler.parser")
1610 # Both will output a message
1611 logger.info(self.next_message())
1612 logger.error(self.next_message())
1613 self.assert_log_lines([
1614 ('INFO', '1'),
1615 ('ERROR', '2'),
1616 ], stream=output)
1617 # Original logger output is empty.
1618 self.assert_log_lines([])
1619 with captured_stdout() as output:
1620 self.apply_config(self.config7)
1621 logger = logging.getLogger("compiler.parser")
1622 self.assertTrue(logger.disabled)
1623 logger = logging.getLogger("compiler.lexer")
1624 # Both will output a message
1625 logger.info(self.next_message())
1626 logger.error(self.next_message())
1627 self.assert_log_lines([
1628 ('INFO', '3'),
1629 ('ERROR', '4'),
1630 ], stream=output)
1631 # Original logger output is empty.
1632 self.assert_log_lines([])
1633
1634 #Same as test_config_7_ok but don't disable old loggers.
1635 def test_config_8_ok(self):
1636 with captured_stdout() as output:
1637 self.apply_config(self.config1)
1638 logger = logging.getLogger("compiler.parser")
1639 # Both will output a message
1640 logger.info(self.next_message())
1641 logger.error(self.next_message())
1642 self.assert_log_lines([
1643 ('INFO', '1'),
1644 ('ERROR', '2'),
1645 ], stream=output)
1646 # Original logger output is empty.
1647 self.assert_log_lines([])
1648 with captured_stdout() as output:
1649 self.apply_config(self.config8)
1650 logger = logging.getLogger("compiler.parser")
1651 self.assertFalse(logger.disabled)
1652 # Both will output a message
1653 logger.info(self.next_message())
1654 logger.error(self.next_message())
1655 logger = logging.getLogger("compiler.lexer")
1656 # Both will output a message
1657 logger.info(self.next_message())
1658 logger.error(self.next_message())
1659 self.assert_log_lines([
1660 ('INFO', '3'),
1661 ('ERROR', '4'),
1662 ('INFO', '5'),
1663 ('ERROR', '6'),
1664 ], stream=output)
1665 # Original logger output is empty.
1666 self.assert_log_lines([])
1667
1668 def test_config_9_ok(self):
1669 with captured_stdout() as output:
1670 self.apply_config(self.config9)
1671 logger = logging.getLogger("compiler.parser")
1672 #Nothing will be output since both handler and logger are set to WARNING
1673 logger.info(self.next_message())
1674 self.assert_log_lines([], stream=output)
1675 self.apply_config(self.config9a)
1676 #Nothing will be output since both handler is still set to WARNING
1677 logger.info(self.next_message())
1678 self.assert_log_lines([], stream=output)
1679 self.apply_config(self.config9b)
1680 #Message should now be output
1681 logger.info(self.next_message())
1682 self.assert_log_lines([
1683 ('INFO', '3'),
1684 ], stream=output)
1685
1686 def test_config_10_ok(self):
1687 with captured_stdout() as output:
1688 self.apply_config(self.config10)
1689 logger = logging.getLogger("compiler.parser")
1690 logger.warning(self.next_message())
1691 logger = logging.getLogger('compiler')
1692 #Not output, because filtered
1693 logger.warning(self.next_message())
1694 logger = logging.getLogger('compiler.lexer')
1695 #Not output, because filtered
1696 logger.warning(self.next_message())
1697 logger = logging.getLogger("compiler.parser.codegen")
1698 #Output, as not filtered
1699 logger.error(self.next_message())
1700 self.assert_log_lines([
1701 ('WARNING', '1'),
1702 ('ERROR', '4'),
1703 ], stream=output)
1704
1705 def test_config11_ok(self):
1706 self.test_config1_ok(self.config11)
1707
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001708 def test_config12_failure(self):
1709 self.assertRaises(Exception, self.apply_config, self.config12)
1710
1711 def test_config13_failure(self):
1712 self.assertRaises(Exception, self.apply_config, self.config13)
1713
Victor Stinner45df8202010-04-28 22:31:17 +00001714 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001715 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001716 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00001717 # Ask for a randomly assigned port (by using port 0)
1718 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001719 t.start()
1720 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00001721 # Now get the port allocated
1722 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001723 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001724 try:
1725 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1726 sock.settimeout(2.0)
1727 sock.connect(('localhost', port))
1728
1729 slen = struct.pack('>L', len(text))
1730 s = slen + text
1731 sentsofar = 0
1732 left = len(s)
1733 while left > 0:
1734 sent = sock.send(s[sentsofar:])
1735 sentsofar += sent
1736 left -= sent
1737 sock.close()
1738 finally:
1739 t.ready.wait(2.0)
1740 logging.config.stopListening()
1741 t.join(2.0)
1742
1743 def test_listen_config_10_ok(self):
1744 with captured_stdout() as output:
1745 self.setup_via_listener(json.dumps(self.config10))
1746 logger = logging.getLogger("compiler.parser")
1747 logger.warning(self.next_message())
1748 logger = logging.getLogger('compiler')
1749 #Not output, because filtered
1750 logger.warning(self.next_message())
1751 logger = logging.getLogger('compiler.lexer')
1752 #Not output, because filtered
1753 logger.warning(self.next_message())
1754 logger = logging.getLogger("compiler.parser.codegen")
1755 #Output, as not filtered
1756 logger.error(self.next_message())
1757 self.assert_log_lines([
1758 ('WARNING', '1'),
1759 ('ERROR', '4'),
1760 ], stream=output)
1761
1762 def test_listen_config_1_ok(self):
1763 with captured_stdout() as output:
1764 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
1765 logger = logging.getLogger("compiler.parser")
1766 # Both will output a message
1767 logger.info(self.next_message())
1768 logger.error(self.next_message())
1769 self.assert_log_lines([
1770 ('INFO', '1'),
1771 ('ERROR', '2'),
1772 ], stream=output)
1773 # Original logger output is empty.
1774 self.assert_log_lines([])
1775
1776
1777class ManagerTest(BaseTest):
1778 def test_manager_loggerclass(self):
1779 logged = []
1780
1781 class MyLogger(logging.Logger):
1782 def _log(self, level, msg, args, exc_info=None, extra=None):
1783 logged.append(msg)
1784
1785 man = logging.Manager(None)
1786 self.assertRaises(TypeError, man.setLoggerClass, int)
1787 man.setLoggerClass(MyLogger)
1788 logger = man.getLogger('test')
1789 logger.warning('should appear in logged')
1790 logging.warning('should not appear in logged')
1791
1792 self.assertEqual(logged, ['should appear in logged'])
1793
1794
Benjamin Peterson22005fc2010-04-11 16:25:06 +00001795class ChildLoggerTest(BaseTest):
1796 def test_child_loggers(self):
1797 r = logging.getLogger()
1798 l1 = logging.getLogger('abc')
1799 l2 = logging.getLogger('def.ghi')
1800 c1 = r.getChild('xyz')
1801 c2 = r.getChild('uvw.xyz')
1802 self.assertTrue(c1 is logging.getLogger('xyz'))
1803 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
1804 c1 = l1.getChild('def')
1805 c2 = c1.getChild('ghi')
1806 c3 = l1.getChild('def.ghi')
1807 self.assertTrue(c1 is logging.getLogger('abc.def'))
1808 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
1809 self.assertTrue(c2 is c3)
1810
1811
Vinay Sajip6fac8172010-10-19 20:44:14 +00001812class DerivedLogRecord(logging.LogRecord):
1813 pass
1814
Vinay Sajip61561522010-12-03 11:50:38 +00001815class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00001816
1817 def setUp(self):
1818 class CheckingFilter(logging.Filter):
1819 def __init__(self, cls):
1820 self.cls = cls
1821
1822 def filter(self, record):
1823 t = type(record)
1824 if t is not self.cls:
1825 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
1826 self.cls)
1827 raise TypeError(msg)
1828 return True
1829
1830 BaseTest.setUp(self)
1831 self.filter = CheckingFilter(DerivedLogRecord)
1832 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00001833 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00001834
1835 def tearDown(self):
1836 self.root_logger.removeFilter(self.filter)
1837 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00001838 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00001839
1840 def test_logrecord_class(self):
1841 self.assertRaises(TypeError, self.root_logger.warning,
1842 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00001843 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00001844 self.root_logger.error(self.next_message())
1845 self.assert_log_lines([
1846 ('root', 'ERROR', '2'),
1847 ])
1848
1849
Vinay Sajip8552d1f2010-09-14 09:34:09 +00001850class QueueHandlerTest(BaseTest):
1851 # Do not bother with a logger name group.
1852 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
1853
1854 def setUp(self):
1855 BaseTest.setUp(self)
1856 self.queue = queue.Queue(-1)
1857 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
1858 self.que_logger = logging.getLogger('que')
1859 self.que_logger.propagate = False
1860 self.que_logger.setLevel(logging.WARNING)
1861 self.que_logger.addHandler(self.que_hdlr)
1862
1863 def tearDown(self):
1864 self.que_hdlr.close()
1865 BaseTest.tearDown(self)
1866
1867 def test_queue_handler(self):
1868 self.que_logger.debug(self.next_message())
1869 self.assertRaises(queue.Empty, self.queue.get_nowait)
1870 self.que_logger.info(self.next_message())
1871 self.assertRaises(queue.Empty, self.queue.get_nowait)
1872 msg = self.next_message()
1873 self.que_logger.warning(msg)
1874 data = self.queue.get_nowait()
1875 self.assertTrue(isinstance(data, logging.LogRecord))
1876 self.assertEqual(data.name, self.que_logger.name)
1877 self.assertEqual((data.msg, data.args), (msg, None))
1878
Vinay Sajipa39c5712010-10-25 13:57:39 +00001879class FormatterTest(unittest.TestCase):
1880 def setUp(self):
1881 self.common = {
1882 'name': 'formatter.test',
1883 'level': logging.DEBUG,
1884 'pathname': os.path.join('path', 'to', 'dummy.ext'),
1885 'lineno': 42,
1886 'exc_info': None,
1887 'func': None,
1888 'msg': 'Message with %d %s',
1889 'args': (2, 'placeholders'),
1890 }
1891 self.variants = {
1892 }
1893
1894 def get_record(self, name=None):
1895 result = dict(self.common)
1896 if name is not None:
1897 result.update(self.variants[name])
1898 return logging.makeLogRecord(result)
1899
1900 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00001901 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00001902 r = self.get_record()
1903 f = logging.Formatter('${%(message)s}')
1904 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
1905 f = logging.Formatter('%(random)s')
1906 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00001907 self.assertFalse(f.usesTime())
1908 f = logging.Formatter('%(asctime)s')
1909 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00001910 f = logging.Formatter('%(asctime)-15s')
1911 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00001912 f = logging.Formatter('asctime')
1913 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00001914
1915 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00001916 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00001917 r = self.get_record()
1918 f = logging.Formatter('$%{message}%$', style='{')
1919 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
1920 f = logging.Formatter('{random}', style='{')
1921 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00001922 self.assertFalse(f.usesTime())
1923 f = logging.Formatter('{asctime}', style='{')
1924 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00001925 f = logging.Formatter('{asctime!s:15}', style='{')
1926 self.assertTrue(f.usesTime())
1927 f = logging.Formatter('{asctime:15}', style='{')
1928 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00001929 f = logging.Formatter('asctime', style='{')
1930 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00001931
1932 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00001933 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00001934 r = self.get_record()
1935 f = logging.Formatter('$message', style='$')
1936 self.assertEqual(f.format(r), 'Message with 2 placeholders')
1937 f = logging.Formatter('$$%${message}%$$', style='$')
1938 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
1939 f = logging.Formatter('${random}', style='$')
1940 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00001941 self.assertFalse(f.usesTime())
1942 f = logging.Formatter('${asctime}', style='$')
1943 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00001944 f = logging.Formatter('${asctime', style='$')
1945 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00001946 f = logging.Formatter('$asctime', style='$')
1947 self.assertTrue(f.usesTime())
1948 f = logging.Formatter('asctime', style='$')
1949 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00001950
Vinay Sajip5a27d402010-12-10 11:42:57 +00001951class LastResortTest(BaseTest):
1952 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00001953 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00001954 root = self.root_logger
1955 root.removeHandler(self.root_hdlr)
1956 old_stderr = sys.stderr
1957 old_lastresort = logging.lastResort
1958 old_raise_exceptions = logging.raiseExceptions
1959 try:
1960 sys.stderr = sio = io.StringIO()
1961 root.warning('This is your final chance!')
1962 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
1963 #No handlers and no last resort, so 'No handlers' message
1964 logging.lastResort = None
1965 sys.stderr = sio = io.StringIO()
1966 root.warning('This is your final chance!')
1967 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
1968 # 'No handlers' message only printed once
1969 sys.stderr = sio = io.StringIO()
1970 root.warning('This is your final chance!')
1971 self.assertEqual(sio.getvalue(), '')
1972 root.manager.emittedNoHandlerWarning = False
1973 #If raiseExceptions is False, no message is printed
1974 logging.raiseExceptions = False
1975 sys.stderr = sio = io.StringIO()
1976 root.warning('This is your final chance!')
1977 self.assertEqual(sio.getvalue(), '')
1978 finally:
1979 sys.stderr = old_stderr
1980 root.addHandler(self.root_hdlr)
1981 logging.lastResort = old_lastresort
1982 logging.raiseExceptions = old_raise_exceptions
1983
1984
Vinay Sajip19ec67a2010-09-17 18:57:36 +00001985class BaseFileTest(BaseTest):
1986 "Base class for handler tests that write log files"
1987
1988 def setUp(self):
1989 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00001990 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
1991 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00001992 self.rmfiles = []
1993
1994 def tearDown(self):
1995 for fn in self.rmfiles:
1996 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00001997 if os.path.exists(self.fn):
1998 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00001999 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002000
2001 def assertLogFile(self, filename):
2002 "Assert a log file is there and register it for deletion"
2003 self.assertTrue(os.path.exists(filename),
2004 msg="Log file %r does not exist")
2005 self.rmfiles.append(filename)
2006
2007
2008class RotatingFileHandlerTest(BaseFileTest):
2009 def next_rec(self):
2010 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2011 self.next_message(), None, None, None)
2012
2013 def test_should_not_rollover(self):
2014 # If maxbytes is zero rollover never occurs
2015 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2016 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002017 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002018
2019 def test_should_rollover(self):
2020 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2021 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002022 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002023
2024 def test_file_created(self):
2025 # checks that the file is created and assumes it was created
2026 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002027 rh = logging.handlers.RotatingFileHandler(self.fn)
2028 rh.emit(self.next_rec())
2029 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002030 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002031
2032 def test_rollover_filenames(self):
2033 rh = logging.handlers.RotatingFileHandler(
2034 self.fn, backupCount=2, maxBytes=1)
2035 rh.emit(self.next_rec())
2036 self.assertLogFile(self.fn)
2037 rh.emit(self.next_rec())
2038 self.assertLogFile(self.fn + ".1")
2039 rh.emit(self.next_rec())
2040 self.assertLogFile(self.fn + ".2")
2041 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002042 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002043
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002044class TimedRotatingFileHandlerTest(BaseFileTest):
2045 # test methods added below
2046 pass
2047
2048def secs(**kw):
2049 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2050
2051for when, exp in (('S', 1),
2052 ('M', 60),
2053 ('H', 60 * 60),
2054 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002055 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002056 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002057 ('W0', secs(days=4, hours=24)),
2058 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002059 def test_compute_rollover(self, when=when, exp=exp):
2060 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002061 self.fn, when=when, interval=1, backupCount=0, utc=True)
2062 currentTime = 0.0
2063 actual = rh.computeRollover(currentTime)
2064 if exp != actual:
2065 # Failures occur on some systems for MIDNIGHT and W0.
2066 # Print detailed calculation for MIDNIGHT so we can try to see
2067 # what's going on
2068 import time
2069 if when == 'MIDNIGHT':
2070 try:
2071 if rh.utc:
2072 t = time.gmtime(currentTime)
2073 else:
2074 t = time.localtime(currentTime)
2075 currentHour = t[3]
2076 currentMinute = t[4]
2077 currentSecond = t[5]
2078 # r is the number of seconds left between now and midnight
2079 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
2080 currentMinute) * 60 +
2081 currentSecond)
2082 result = currentTime + r
2083 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
2084 print('currentHour: %s' % currentHour, file=sys.stderr)
2085 print('currentMinute: %s' % currentMinute, file=sys.stderr)
2086 print('currentSecond: %s' % currentSecond, file=sys.stderr)
2087 print('r: %s' % r, file=sys.stderr)
2088 print('result: %s' % result, file=sys.stderr)
2089 except Exception:
2090 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
2091 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00002092 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002093 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
2094
Christian Heimes180510d2008-03-03 19:15:45 +00002095# Set the locale to the platform-dependent default. I have no idea
2096# why the test does this, but in any case we save the current locale
2097# first and restore it at the end.
2098@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00002099def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00002100 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002101 CustomLevelsAndFiltersTest, MemoryHandlerTest,
2102 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002103 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajipa39c5712010-10-25 13:57:39 +00002104 FormatterTest,
Vinay Sajip61561522010-12-03 11:50:38 +00002105 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipbc85d842010-09-17 23:35:29 +00002106 RotatingFileHandlerTest,
Vinay Sajip5a27d402010-12-10 11:42:57 +00002107 LastResortTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00002108 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00002109 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00002110
Christian Heimes180510d2008-03-03 19:15:45 +00002111if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002112 test_main()