blob: 2aac3603dceddc6ddeaaf0baece5a2f4455b3e65 [file] [log] [blame]
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001#!/usr/bin/env python
Brett Cannon56c4deb2008-03-03 00:38:58 +00002#
Georg Brandl8cdc9bc2010-01-01 13:07:05 +00003# Copyright 2001-2010 by Vinay Sajip. All Rights Reserved.
Brett Cannon56c4deb2008-03-03 00:38:58 +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
Georg Brandl8cdc9bc2010-01-01 13:07:05 +000021Copyright (C) 2001-2010 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Brett Cannon56c4deb2008-03-03 00:38:58 +000024import logging
25import logging.handlers
26import logging.config
Brett Cannonf9db8a32008-02-17 01:59:18 +000027
Vinay Sajipb20af942009-02-08 19:06:08 +000028import codecs
Brett Cannon56c4deb2008-03-03 00:38:58 +000029import cPickle
30import cStringIO
31import gc
Vinay Sajip28c382f2010-02-04 18:48:53 +000032import json
Brett Cannon56c4deb2008-03-03 00:38:58 +000033import os
34import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000035import select
Brett Cannon56c4deb2008-03-03 00:38:58 +000036import socket
Georg Brandle152a772008-05-24 18:31:28 +000037from SocketServer import ThreadingTCPServer, StreamRequestHandler
Brett Cannon56c4deb2008-03-03 00:38:58 +000038import struct
39import sys
40import tempfile
Vinay Sajip27a13702010-05-03 15:11:53 +000041from test.test_support import captured_stdout, run_with_locale, run_unittest
Brett Cannon56c4deb2008-03-03 00:38:58 +000042import textwrap
Brett Cannon56c4deb2008-03-03 00:38:58 +000043import unittest
Vinay Sajip213faca2008-12-03 23:22:58 +000044import warnings
Brett Cannon56c4deb2008-03-03 00:38:58 +000045import weakref
Victor Stinner6a102812010-04-27 23:55:59 +000046try:
47 import threading
48except ImportError:
49 threading = None
Brett Cannonf9db8a32008-02-17 01:59:18 +000050
Brett Cannon56c4deb2008-03-03 00:38:58 +000051class BaseTest(unittest.TestCase):
52
53 """Base class for logging tests."""
54
55 log_format = "%(name)s -> %(levelname)s: %(message)s"
56 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
57 message_num = 0
58
59 def setUp(self):
60 """Setup the default logging stream to an internal StringIO instance,
61 so that we can examine log output as we want."""
62 logger_dict = logging.getLogger().manager.loggerDict
Brett Cannonf9db8a32008-02-17 01:59:18 +000063 logging._acquireLock()
64 try:
Brett Cannon56c4deb2008-03-03 00:38:58 +000065 self.saved_handlers = logging._handlers.copy()
66 self.saved_handler_list = logging._handlerList[:]
67 self.saved_loggers = logger_dict.copy()
68 self.saved_level_names = logging._levelNames.copy()
Brett Cannonf9db8a32008-02-17 01:59:18 +000069 finally:
70 logging._releaseLock()
71
Vinay Sajip31e928e2010-03-22 13:02:28 +000072 # Set two unused loggers: one non-ASCII and one Unicode.
73 # This is to test correct operation when sorting existing
74 # loggers in the configuration code. See issue 8201.
75 logging.getLogger("\xab\xd7\xbb")
76 logging.getLogger(u"\u013f\u00d6\u0047")
77
Brett Cannon56c4deb2008-03-03 00:38:58 +000078 self.root_logger = logging.getLogger("")
79 self.original_logging_level = self.root_logger.getEffectiveLevel()
80
81 self.stream = cStringIO.StringIO()
82 self.root_logger.setLevel(logging.DEBUG)
83 self.root_hdlr = logging.StreamHandler(self.stream)
84 self.root_formatter = logging.Formatter(self.log_format)
85 self.root_hdlr.setFormatter(self.root_formatter)
86 self.root_logger.addHandler(self.root_hdlr)
87
88 def tearDown(self):
89 """Remove our logging stream, and restore the original logging
90 level."""
91 self.stream.close()
92 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajip28c382f2010-02-04 18:48:53 +000093 while self.root_logger.handlers:
94 h = self.root_logger.handlers[0]
95 self.root_logger.removeHandler(h)
96 h.close()
Brett Cannon56c4deb2008-03-03 00:38:58 +000097 self.root_logger.setLevel(self.original_logging_level)
98 logging._acquireLock()
99 try:
100 logging._levelNames.clear()
101 logging._levelNames.update(self.saved_level_names)
102 logging._handlers.clear()
103 logging._handlers.update(self.saved_handlers)
104 logging._handlerList[:] = self.saved_handler_list
105 loggerDict = logging.getLogger().manager.loggerDict
106 loggerDict.clear()
107 loggerDict.update(self.saved_loggers)
108 finally:
109 logging._releaseLock()
110
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000111 def assert_log_lines(self, expected_values, stream=None):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000112 """Match the collected log lines against the regular expression
113 self.expected_log_pat, and compare the extracted group values to
114 the expected_values list of tuples."""
115 stream = stream or self.stream
116 pat = re.compile(self.expected_log_pat)
117 try:
118 stream.reset()
119 actual_lines = stream.readlines()
120 except AttributeError:
121 # StringIO.StringIO lacks a reset() method.
122 actual_lines = stream.getvalue().splitlines()
Ezio Melotti2623a372010-11-21 13:34:58 +0000123 self.assertEqual(len(actual_lines), len(expected_values))
Brett Cannon56c4deb2008-03-03 00:38:58 +0000124 for actual, expected in zip(actual_lines, expected_values):
125 match = pat.search(actual)
126 if not match:
127 self.fail("Log line does not match expected pattern:\n" +
128 actual)
Ezio Melotti2623a372010-11-21 13:34:58 +0000129 self.assertEqual(tuple(match.groups()), expected)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000130 s = stream.read()
131 if s:
132 self.fail("Remaining output at end of log stream:\n" + s)
133
134 def next_message(self):
135 """Generate a message consisting solely of an auto-incrementing
136 integer."""
137 self.message_num += 1
138 return "%d" % self.message_num
139
140
141class BuiltinLevelsTest(BaseTest):
142 """Test builtin levels and their inheritance."""
143
144 def test_flat(self):
145 #Logging levels in a flat logger namespace.
146 m = self.next_message
147
148 ERR = logging.getLogger("ERR")
149 ERR.setLevel(logging.ERROR)
150 INF = logging.getLogger("INF")
151 INF.setLevel(logging.INFO)
152 DEB = logging.getLogger("DEB")
153 DEB.setLevel(logging.DEBUG)
154
155 # These should log.
156 ERR.log(logging.CRITICAL, m())
157 ERR.error(m())
158
159 INF.log(logging.CRITICAL, m())
160 INF.error(m())
161 INF.warn(m())
162 INF.info(m())
163
164 DEB.log(logging.CRITICAL, m())
165 DEB.error(m())
166 DEB.warn (m())
167 DEB.info (m())
168 DEB.debug(m())
169
170 # These should not log.
171 ERR.warn(m())
172 ERR.info(m())
173 ERR.debug(m())
174
175 INF.debug(m())
176
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000177 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000178 ('ERR', 'CRITICAL', '1'),
179 ('ERR', 'ERROR', '2'),
180 ('INF', 'CRITICAL', '3'),
181 ('INF', 'ERROR', '4'),
182 ('INF', 'WARNING', '5'),
183 ('INF', 'INFO', '6'),
184 ('DEB', 'CRITICAL', '7'),
185 ('DEB', 'ERROR', '8'),
186 ('DEB', 'WARNING', '9'),
187 ('DEB', 'INFO', '10'),
188 ('DEB', 'DEBUG', '11'),
189 ])
190
191 def test_nested_explicit(self):
192 # Logging levels in a nested namespace, all explicitly set.
193 m = self.next_message
194
195 INF = logging.getLogger("INF")
196 INF.setLevel(logging.INFO)
197 INF_ERR = logging.getLogger("INF.ERR")
198 INF_ERR.setLevel(logging.ERROR)
199
200 # These should log.
201 INF_ERR.log(logging.CRITICAL, m())
202 INF_ERR.error(m())
203
204 # These should not log.
205 INF_ERR.warn(m())
206 INF_ERR.info(m())
207 INF_ERR.debug(m())
208
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000209 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000210 ('INF.ERR', 'CRITICAL', '1'),
211 ('INF.ERR', 'ERROR', '2'),
212 ])
213
214 def test_nested_inherited(self):
215 #Logging levels in a nested namespace, inherited from parent loggers.
216 m = self.next_message
217
218 INF = logging.getLogger("INF")
219 INF.setLevel(logging.INFO)
220 INF_ERR = logging.getLogger("INF.ERR")
221 INF_ERR.setLevel(logging.ERROR)
222 INF_UNDEF = logging.getLogger("INF.UNDEF")
223 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
224 UNDEF = logging.getLogger("UNDEF")
225
226 # These should log.
227 INF_UNDEF.log(logging.CRITICAL, m())
228 INF_UNDEF.error(m())
229 INF_UNDEF.warn(m())
230 INF_UNDEF.info(m())
231 INF_ERR_UNDEF.log(logging.CRITICAL, m())
232 INF_ERR_UNDEF.error(m())
233
234 # These should not log.
235 INF_UNDEF.debug(m())
236 INF_ERR_UNDEF.warn(m())
237 INF_ERR_UNDEF.info(m())
238 INF_ERR_UNDEF.debug(m())
239
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000240 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000241 ('INF.UNDEF', 'CRITICAL', '1'),
242 ('INF.UNDEF', 'ERROR', '2'),
243 ('INF.UNDEF', 'WARNING', '3'),
244 ('INF.UNDEF', 'INFO', '4'),
245 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
246 ('INF.ERR.UNDEF', 'ERROR', '6'),
247 ])
248
249 def test_nested_with_virtual_parent(self):
250 # Logging levels when some parent does not exist yet.
251 m = self.next_message
252
253 INF = logging.getLogger("INF")
254 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
255 CHILD = logging.getLogger("INF.BADPARENT")
256 INF.setLevel(logging.INFO)
257
258 # These should log.
259 GRANDCHILD.log(logging.FATAL, m())
260 GRANDCHILD.info(m())
261 CHILD.log(logging.FATAL, m())
262 CHILD.info(m())
263
264 # These should not log.
265 GRANDCHILD.debug(m())
266 CHILD.debug(m())
267
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000268 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000269 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
270 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
271 ('INF.BADPARENT', 'CRITICAL', '3'),
272 ('INF.BADPARENT', 'INFO', '4'),
273 ])
274
Vinay Sajip74ab3442011-11-07 08:49:16 +0000275 def test_invalid_name(self):
Vinay Sajip53703d92011-11-07 10:13:18 +0000276 self.assertRaises(TypeError, logging.getLogger, any)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000277
278class BasicFilterTest(BaseTest):
279
280 """Test the bundled Filter class."""
281
282 def test_filter(self):
283 # Only messages satisfying the specified criteria pass through the
284 # filter.
285 filter_ = logging.Filter("spam.eggs")
286 handler = self.root_logger.handlers[0]
287 try:
288 handler.addFilter(filter_)
289 spam = logging.getLogger("spam")
290 spam_eggs = logging.getLogger("spam.eggs")
291 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
292 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
293
294 spam.info(self.next_message())
295 spam_eggs.info(self.next_message()) # Good.
296 spam_eggs_fish.info(self.next_message()) # Good.
297 spam_bakedbeans.info(self.next_message())
298
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000299 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000300 ('spam.eggs', 'INFO', '2'),
301 ('spam.eggs.fish', 'INFO', '3'),
302 ])
303 finally:
304 handler.removeFilter(filter_)
305
306
307#
308# First, we define our levels. There can be as many as you want - the only
309# limitations are that they should be integers, the lowest should be > 0 and
310# larger values mean less information being logged. If you need specific
311# level values which do not fit into these limitations, you can use a
312# mapping dictionary to convert between your application levels and the
313# logging system.
314#
315SILENT = 120
316TACITURN = 119
317TERSE = 118
318EFFUSIVE = 117
319SOCIABLE = 116
320VERBOSE = 115
321TALKATIVE = 114
322GARRULOUS = 113
323CHATTERBOX = 112
324BORING = 111
325
326LEVEL_RANGE = range(BORING, SILENT + 1)
327
328#
329# Next, we define names for our levels. You don't need to do this - in which
330# case the system will use "Level n" to denote the text for the level.
331#
332my_logging_levels = {
333 SILENT : 'Silent',
334 TACITURN : 'Taciturn',
335 TERSE : 'Terse',
336 EFFUSIVE : 'Effusive',
337 SOCIABLE : 'Sociable',
338 VERBOSE : 'Verbose',
339 TALKATIVE : 'Talkative',
340 GARRULOUS : 'Garrulous',
341 CHATTERBOX : 'Chatterbox',
342 BORING : 'Boring',
343}
344
345class GarrulousFilter(logging.Filter):
346
347 """A filter which blocks garrulous messages."""
348
349 def filter(self, record):
350 return record.levelno != GARRULOUS
351
352class VerySpecificFilter(logging.Filter):
353
354 """A filter which blocks sociable and taciturn messages."""
355
356 def filter(self, record):
357 return record.levelno not in [SOCIABLE, TACITURN]
358
359
360class CustomLevelsAndFiltersTest(BaseTest):
361
362 """Test various filtering possibilities with custom logging levels."""
363
364 # Skip the logger name group.
365 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
366
367 def setUp(self):
368 BaseTest.setUp(self)
369 for k, v in my_logging_levels.items():
370 logging.addLevelName(k, v)
371
372 def log_at_all_levels(self, logger):
373 for lvl in LEVEL_RANGE:
374 logger.log(lvl, self.next_message())
375
376 def test_logger_filter(self):
377 # Filter at logger level.
378 self.root_logger.setLevel(VERBOSE)
379 # Levels >= 'Verbose' are good.
380 self.log_at_all_levels(self.root_logger)
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000381 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000382 ('Verbose', '5'),
383 ('Sociable', '6'),
384 ('Effusive', '7'),
385 ('Terse', '8'),
386 ('Taciturn', '9'),
387 ('Silent', '10'),
388 ])
389
390 def test_handler_filter(self):
391 # Filter at handler level.
392 self.root_logger.handlers[0].setLevel(SOCIABLE)
393 try:
394 # Levels >= 'Sociable' are good.
395 self.log_at_all_levels(self.root_logger)
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000396 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000397 ('Sociable', '6'),
398 ('Effusive', '7'),
399 ('Terse', '8'),
400 ('Taciturn', '9'),
401 ('Silent', '10'),
402 ])
403 finally:
404 self.root_logger.handlers[0].setLevel(logging.NOTSET)
405
406 def test_specific_filters(self):
407 # Set a specific filter object on the handler, and then add another
408 # filter object on the logger itself.
409 handler = self.root_logger.handlers[0]
410 specific_filter = None
411 garr = GarrulousFilter()
412 handler.addFilter(garr)
413 try:
414 self.log_at_all_levels(self.root_logger)
415 first_lines = [
416 # Notice how 'Garrulous' is missing
417 ('Boring', '1'),
418 ('Chatterbox', '2'),
419 ('Talkative', '4'),
420 ('Verbose', '5'),
421 ('Sociable', '6'),
422 ('Effusive', '7'),
423 ('Terse', '8'),
424 ('Taciturn', '9'),
425 ('Silent', '10'),
426 ]
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000427 self.assert_log_lines(first_lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000428
429 specific_filter = VerySpecificFilter()
430 self.root_logger.addFilter(specific_filter)
431 self.log_at_all_levels(self.root_logger)
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000432 self.assert_log_lines(first_lines + [
Brett Cannon56c4deb2008-03-03 00:38:58 +0000433 # Not only 'Garrulous' is still missing, but also 'Sociable'
434 # and 'Taciturn'
435 ('Boring', '11'),
436 ('Chatterbox', '12'),
437 ('Talkative', '14'),
438 ('Verbose', '15'),
439 ('Effusive', '17'),
440 ('Terse', '18'),
441 ('Silent', '20'),
442 ])
443 finally:
444 if specific_filter:
445 self.root_logger.removeFilter(specific_filter)
446 handler.removeFilter(garr)
447
448
449class MemoryHandlerTest(BaseTest):
450
451 """Tests for the MemoryHandler."""
452
453 # Do not bother with a logger name group.
454 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
455
456 def setUp(self):
457 BaseTest.setUp(self)
458 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
459 self.root_hdlr)
460 self.mem_logger = logging.getLogger('mem')
461 self.mem_logger.propagate = 0
462 self.mem_logger.addHandler(self.mem_hdlr)
463
464 def tearDown(self):
465 self.mem_hdlr.close()
Amaury Forgeot d'Arc2aece572008-03-29 01:42:31 +0000466 BaseTest.tearDown(self)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000467
468 def test_flush(self):
469 # The memory handler flushes to its target handler based on specific
470 # criteria (message count and message level).
471 self.mem_logger.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000472 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000473 self.mem_logger.info(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000474 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000475 # This will flush because the level is >= logging.WARNING
476 self.mem_logger.warn(self.next_message())
477 lines = [
478 ('DEBUG', '1'),
479 ('INFO', '2'),
480 ('WARNING', '3'),
481 ]
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000482 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000483 for n in (4, 14):
484 for i in range(9):
485 self.mem_logger.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000486 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000487 # This will flush because it's the 10th message since the last
488 # flush.
489 self.mem_logger.debug(self.next_message())
490 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000491 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000492
493 self.mem_logger.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000494 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000495
496
497class ExceptionFormatter(logging.Formatter):
498 """A special exception formatter."""
499 def formatException(self, ei):
500 return "Got a [%s]" % ei[0].__name__
501
502
503class ConfigFileTest(BaseTest):
504
505 """Reading logging config from a .ini-style config file."""
506
507 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
508
509 # config0 is a standard configuration.
510 config0 = """
511 [loggers]
512 keys=root
513
514 [handlers]
515 keys=hand1
516
517 [formatters]
518 keys=form1
519
520 [logger_root]
521 level=WARNING
522 handlers=hand1
523
524 [handler_hand1]
525 class=StreamHandler
526 level=NOTSET
527 formatter=form1
528 args=(sys.stdout,)
529
530 [formatter_form1]
531 format=%(levelname)s ++ %(message)s
532 datefmt=
533 """
534
535 # config1 adds a little to the standard configuration.
536 config1 = """
537 [loggers]
538 keys=root,parser
539
540 [handlers]
541 keys=hand1
542
543 [formatters]
544 keys=form1
545
546 [logger_root]
547 level=WARNING
548 handlers=
549
550 [logger_parser]
551 level=DEBUG
552 handlers=hand1
553 propagate=1
554 qualname=compiler.parser
555
556 [handler_hand1]
557 class=StreamHandler
558 level=NOTSET
559 formatter=form1
560 args=(sys.stdout,)
561
562 [formatter_form1]
563 format=%(levelname)s ++ %(message)s
564 datefmt=
565 """
566
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000567 # config1a moves the handler to the root.
568 config1a = """
569 [loggers]
570 keys=root,parser
571
572 [handlers]
573 keys=hand1
574
575 [formatters]
576 keys=form1
577
578 [logger_root]
579 level=WARNING
580 handlers=hand1
581
582 [logger_parser]
583 level=DEBUG
584 handlers=
585 propagate=1
586 qualname=compiler.parser
587
588 [handler_hand1]
589 class=StreamHandler
590 level=NOTSET
591 formatter=form1
592 args=(sys.stdout,)
593
594 [formatter_form1]
595 format=%(levelname)s ++ %(message)s
596 datefmt=
597 """
598
Brett Cannon56c4deb2008-03-03 00:38:58 +0000599 # config2 has a subtle configuration error that should be reported
600 config2 = config1.replace("sys.stdout", "sys.stbout")
601
602 # config3 has a less subtle configuration error
603 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
604
605 # config4 specifies a custom formatter class to be loaded
606 config4 = """
607 [loggers]
608 keys=root
609
610 [handlers]
611 keys=hand1
612
613 [formatters]
614 keys=form1
615
616 [logger_root]
617 level=NOTSET
618 handlers=hand1
619
620 [handler_hand1]
621 class=StreamHandler
622 level=NOTSET
623 formatter=form1
624 args=(sys.stdout,)
625
626 [formatter_form1]
627 class=""" + __name__ + """.ExceptionFormatter
628 format=%(levelname)s:%(name)s:%(message)s
629 datefmt=
630 """
631
Vinay Sajip70fdc952008-07-18 09:00:00 +0000632 # config5 specifies a custom handler class to be loaded
633 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
634
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000635 # config6 uses ', ' delimiters in the handlers and formatters sections
636 config6 = """
637 [loggers]
638 keys=root,parser
639
640 [handlers]
641 keys=hand1, hand2
642
643 [formatters]
644 keys=form1, form2
645
646 [logger_root]
647 level=WARNING
648 handlers=
649
650 [logger_parser]
651 level=DEBUG
652 handlers=hand1
653 propagate=1
654 qualname=compiler.parser
655
656 [handler_hand1]
657 class=StreamHandler
658 level=NOTSET
659 formatter=form1
660 args=(sys.stdout,)
661
662 [handler_hand2]
Vinay Sajip844f7412008-09-09 13:42:08 +0000663 class=StreamHandler
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000664 level=NOTSET
665 formatter=form1
Vinay Sajip844f7412008-09-09 13:42:08 +0000666 args=(sys.stderr,)
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000667
668 [formatter_form1]
669 format=%(levelname)s ++ %(message)s
670 datefmt=
671
672 [formatter_form2]
673 format=%(message)s
674 datefmt=
675 """
676
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000677 # config7 adds a compiler logger.
678 config7 = """
679 [loggers]
680 keys=root,parser,compiler
681
682 [handlers]
683 keys=hand1
684
685 [formatters]
686 keys=form1
687
688 [logger_root]
689 level=WARNING
690 handlers=hand1
691
692 [logger_compiler]
693 level=DEBUG
694 handlers=
695 propagate=1
696 qualname=compiler
697
698 [logger_parser]
699 level=DEBUG
700 handlers=
701 propagate=1
702 qualname=compiler.parser
703
704 [handler_hand1]
705 class=StreamHandler
706 level=NOTSET
707 formatter=form1
708 args=(sys.stdout,)
709
710 [formatter_form1]
711 format=%(levelname)s ++ %(message)s
712 datefmt=
713 """
714
Brett Cannon56c4deb2008-03-03 00:38:58 +0000715 def apply_config(self, conf):
Vinay Sajip28c382f2010-02-04 18:48:53 +0000716 file = cStringIO.StringIO(textwrap.dedent(conf))
717 logging.config.fileConfig(file)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000718
719 def test_config0_ok(self):
720 # A simple config file which overrides the default settings.
721 with captured_stdout() as output:
722 self.apply_config(self.config0)
723 logger = logging.getLogger()
724 # Won't output anything
725 logger.info(self.next_message())
726 # Outputs a message
727 logger.error(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000728 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000729 ('ERROR', '2'),
730 ], stream=output)
731 # Original logger output is empty.
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000732 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000733
Vinay Sajip70fdc952008-07-18 09:00:00 +0000734 def test_config1_ok(self, config=config1):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000735 # A config file defining a sub-parser as well.
736 with captured_stdout() as output:
Vinay Sajip70fdc952008-07-18 09:00:00 +0000737 self.apply_config(config)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000738 logger = logging.getLogger("compiler.parser")
739 # Both will output a message
740 logger.info(self.next_message())
741 logger.error(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000742 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000743 ('INFO', '1'),
744 ('ERROR', '2'),
745 ], stream=output)
746 # Original logger output is empty.
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000747 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000748
749 def test_config2_failure(self):
750 # A simple config file which overrides the default settings.
751 self.assertRaises(StandardError, self.apply_config, self.config2)
752
753 def test_config3_failure(self):
754 # A simple config file which overrides the default settings.
755 self.assertRaises(StandardError, self.apply_config, self.config3)
756
757 def test_config4_ok(self):
758 # A config file specifying a custom formatter class.
759 with captured_stdout() as output:
760 self.apply_config(self.config4)
761 logger = logging.getLogger()
762 try:
763 raise RuntimeError()
764 except RuntimeError:
765 logging.exception("just testing")
766 sys.stdout.seek(0)
Ezio Melotti2623a372010-11-21 13:34:58 +0000767 self.assertEqual(output.getvalue(),
Brett Cannon56c4deb2008-03-03 00:38:58 +0000768 "ERROR:root:just testing\nGot a [RuntimeError]\n")
769 # Original logger output is empty
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000770 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000771
Vinay Sajip70fdc952008-07-18 09:00:00 +0000772 def test_config5_ok(self):
773 self.test_config1_ok(config=self.config5)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000774
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000775 def test_config6_ok(self):
776 self.test_config1_ok(config=self.config6)
777
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000778 def test_config7_ok(self):
779 with captured_stdout() as output:
780 self.apply_config(self.config1a)
781 logger = logging.getLogger("compiler.parser")
782 # See issue #11424. compiler-hyphenated sorts
783 # between compiler and compiler.xyz and this
784 # was preventing compiler.xyz from being included
785 # in the child loggers of compiler because of an
786 # overzealous loop termination condition.
787 hyphenated = logging.getLogger('compiler-hyphenated')
788 # All will output a message
789 logger.info(self.next_message())
790 logger.error(self.next_message())
791 hyphenated.critical(self.next_message())
792 self.assert_log_lines([
793 ('INFO', '1'),
794 ('ERROR', '2'),
795 ('CRITICAL', '3'),
796 ], stream=output)
797 # Original logger output is empty.
798 self.assert_log_lines([])
799 with captured_stdout() as output:
800 self.apply_config(self.config7)
801 logger = logging.getLogger("compiler.parser")
802 self.assertFalse(logger.disabled)
803 # Both will output a message
804 logger.info(self.next_message())
805 logger.error(self.next_message())
806 logger = logging.getLogger("compiler.lexer")
807 # Both will output a message
808 logger.info(self.next_message())
809 logger.error(self.next_message())
810 # Will not appear
811 hyphenated.critical(self.next_message())
812 self.assert_log_lines([
813 ('INFO', '4'),
814 ('ERROR', '5'),
815 ('INFO', '6'),
816 ('ERROR', '7'),
817 ], stream=output)
818 # Original logger output is empty.
819 self.assert_log_lines([])
Vinay Sajip923e6d22011-03-07 18:20:27 +0000820
Brett Cannon56c4deb2008-03-03 00:38:58 +0000821class LogRecordStreamHandler(StreamRequestHandler):
822
823 """Handler for a streaming logging request. It saves the log message in the
824 TCP server's 'log_output' attribute."""
825
826 TCP_LOG_END = "!!!END!!!"
827
828 def handle(self):
829 """Handle multiple requests - each expected to be of 4-byte length,
830 followed by the LogRecord in pickle format. Logs the record
831 according to whatever policy is configured locally."""
832 while True:
833 chunk = self.connection.recv(4)
834 if len(chunk) < 4:
835 break
836 slen = struct.unpack(">L", chunk)[0]
837 chunk = self.connection.recv(slen)
838 while len(chunk) < slen:
839 chunk = chunk + self.connection.recv(slen - len(chunk))
840 obj = self.unpickle(chunk)
841 record = logging.makeLogRecord(obj)
842 self.handle_log_record(record)
843
844 def unpickle(self, data):
845 return cPickle.loads(data)
846
847 def handle_log_record(self, record):
848 # If the end-of-messages sentinel is seen, tell the server to
849 # terminate.
850 if self.TCP_LOG_END in record.msg:
851 self.server.abort = 1
852 return
853 self.server.log_output += record.msg + "\n"
854
Guido van Rossum376e6362003-04-25 14:22:00 +0000855
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000856class LogRecordSocketReceiver(ThreadingTCPServer):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000857
858 """A simple-minded TCP socket-based logging receiver suitable for test
859 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000860
861 allow_reuse_address = 1
Brett Cannon56c4deb2008-03-03 00:38:58 +0000862 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000863
864 def __init__(self, host='localhost',
865 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
866 handler=LogRecordStreamHandler):
867 ThreadingTCPServer.__init__(self, (host, port), handler)
Georg Brandl57826cf2008-02-23 15:06:25 +0000868 self.abort = False
Brett Cannon56c4deb2008-03-03 00:38:58 +0000869 self.timeout = 0.1
870 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000871
872 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000873 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000874 rd, wr, ex = select.select([self.socket.fileno()], [], [],
875 self.timeout)
876 if rd:
877 self.handle_request()
Brett Cannon56c4deb2008-03-03 00:38:58 +0000878 # Notify the main thread that we're about to exit
879 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000880 # close the listen socket
881 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000882
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000883
Victor Stinner6a102812010-04-27 23:55:59 +0000884@unittest.skipUnless(threading, 'Threading required for this test.')
Brett Cannon56c4deb2008-03-03 00:38:58 +0000885class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000886
Brett Cannon56c4deb2008-03-03 00:38:58 +0000887 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000888
Brett Cannon56c4deb2008-03-03 00:38:58 +0000889 def setUp(self):
890 """Set up a TCP server to receive log messages, and a SocketHandler
891 pointing to that server's address and port."""
892 BaseTest.setUp(self)
893 self.tcpserver = LogRecordSocketReceiver(port=0)
894 self.port = self.tcpserver.socket.getsockname()[1]
895 self.threads = [
896 threading.Thread(target=self.tcpserver.serve_until_stopped)]
897 for thread in self.threads:
898 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000899
Brett Cannon56c4deb2008-03-03 00:38:58 +0000900 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
901 self.sock_hdlr.setFormatter(self.root_formatter)
902 self.root_logger.removeHandler(self.root_logger.handlers[0])
903 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000904
Brett Cannon56c4deb2008-03-03 00:38:58 +0000905 def tearDown(self):
906 """Shutdown the TCP server."""
907 try:
908 self.tcpserver.abort = True
909 del self.tcpserver
910 self.root_logger.removeHandler(self.sock_hdlr)
911 self.sock_hdlr.close()
912 for thread in self.threads:
913 thread.join(2.0)
914 finally:
915 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000916
Brett Cannon56c4deb2008-03-03 00:38:58 +0000917 def get_output(self):
918 """Get the log output as received by the TCP server."""
919 # Signal the TCP receiver and wait for it to terminate.
920 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
921 self.tcpserver.finished.wait(2.0)
922 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000923
Brett Cannon56c4deb2008-03-03 00:38:58 +0000924 def test_output(self):
925 # The log message sent to the SocketHandler is properly received.
926 logger = logging.getLogger("tcp")
927 logger.error("spam")
928 logger.debug("eggs")
Ezio Melotti2623a372010-11-21 13:34:58 +0000929 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000930
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000931
Brett Cannon56c4deb2008-03-03 00:38:58 +0000932class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000933
Brett Cannon56c4deb2008-03-03 00:38:58 +0000934 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000935
Brett Cannon56c4deb2008-03-03 00:38:58 +0000936 def setUp(self):
937 """Create a dict to remember potentially destroyed objects."""
938 BaseTest.setUp(self)
939 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000940
Brett Cannon56c4deb2008-03-03 00:38:58 +0000941 def _watch_for_survival(self, *args):
942 """Watch the given objects for survival, by creating weakrefs to
943 them."""
944 for obj in args:
945 key = id(obj), repr(obj)
946 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000947
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000948 def _assertTruesurvival(self):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000949 """Assert that all objects watched for survival have survived."""
950 # Trigger cycle breaking.
951 gc.collect()
952 dead = []
953 for (id_, repr_), ref in self._survivors.items():
954 if ref() is None:
955 dead.append(repr_)
956 if dead:
957 self.fail("%d objects should have survived "
958 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000959
Brett Cannon56c4deb2008-03-03 00:38:58 +0000960 def test_persistent_loggers(self):
961 # Logger objects are persistent and retain their configuration, even
962 # if visible references are destroyed.
963 self.root_logger.setLevel(logging.INFO)
964 foo = logging.getLogger("foo")
965 self._watch_for_survival(foo)
966 foo.setLevel(logging.DEBUG)
967 self.root_logger.debug(self.next_message())
968 foo.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000969 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000970 ('foo', 'DEBUG', '2'),
971 ])
972 del foo
973 # foo has survived.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000974 self._assertTruesurvival()
Brett Cannon56c4deb2008-03-03 00:38:58 +0000975 # foo has retained its settings.
976 bar = logging.getLogger("foo")
977 bar.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000978 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000979 ('foo', 'DEBUG', '2'),
980 ('foo', 'DEBUG', '3'),
981 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000982
Vinay Sajipb20af942009-02-08 19:06:08 +0000983
Vinay Sajip65d66e12008-09-04 07:31:21 +0000984class EncodingTest(BaseTest):
985 def test_encoding_plain_file(self):
986 # In Python 2.x, a plain file object is treated as having no encoding.
987 log = logging.getLogger("test")
988 fn = tempfile.mktemp(".log")
989 # the non-ascii data we write to the log.
990 data = "foo\x80"
991 try:
992 handler = logging.FileHandler(fn)
993 log.addHandler(handler)
994 try:
995 # write non-ascii data to the log.
996 log.warning(data)
997 finally:
998 log.removeHandler(handler)
999 handler.close()
1000 # check we wrote exactly those bytes, ignoring trailing \n etc
1001 f = open(fn)
1002 try:
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001003 self.assertEqual(f.read().rstrip(), data)
Vinay Sajip65d66e12008-09-04 07:31:21 +00001004 finally:
1005 f.close()
1006 finally:
1007 if os.path.isfile(fn):
1008 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001009
Vinay Sajipb20af942009-02-08 19:06:08 +00001010 def test_encoding_cyrillic_unicode(self):
1011 log = logging.getLogger("test")
1012 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1013 message = u'\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1014 #Ensure it's written in a Cyrillic encoding
1015 writer_class = codecs.getwriter('cp1251')
Vinay Sajip74f04502009-04-16 19:07:37 +00001016 writer_class.encoding = 'cp1251'
Vinay Sajipb20af942009-02-08 19:06:08 +00001017 stream = cStringIO.StringIO()
1018 writer = writer_class(stream, 'strict')
1019 handler = logging.StreamHandler(writer)
1020 log.addHandler(handler)
1021 try:
1022 log.warning(message)
1023 finally:
1024 log.removeHandler(handler)
1025 handler.close()
1026 # check we wrote exactly those bytes, ignoring trailing \n etc
1027 s = stream.getvalue()
1028 #Compare against what the data should be when encoded in CP-1251
1029 self.assertEqual(s, '\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1030
1031
Vinay Sajip213faca2008-12-03 23:22:58 +00001032class WarningsTest(BaseTest):
Brett Cannon2da4d622009-04-01 19:57:10 +00001033
Vinay Sajip213faca2008-12-03 23:22:58 +00001034 def test_warnings(self):
Brett Cannon2da4d622009-04-01 19:57:10 +00001035 with warnings.catch_warnings():
Vinay Sajip1e566ce2009-04-05 11:06:24 +00001036 logging.captureWarnings(True)
Brett Cannon2da4d622009-04-01 19:57:10 +00001037 try:
Vinay Sajip14bf0a02009-04-07 17:18:24 +00001038 warnings.filterwarnings("always", category=UserWarning)
Brett Cannon2da4d622009-04-01 19:57:10 +00001039 file = cStringIO.StringIO()
1040 h = logging.StreamHandler(file)
1041 logger = logging.getLogger("py.warnings")
1042 logger.addHandler(h)
1043 warnings.warn("I'm warning you...")
1044 logger.removeHandler(h)
1045 s = file.getvalue()
1046 h.close()
1047 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Vinay Sajip213faca2008-12-03 23:22:58 +00001048
Brett Cannon2da4d622009-04-01 19:57:10 +00001049 #See if an explicit file uses the original implementation
1050 file = cStringIO.StringIO()
1051 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1052 file, "Dummy line")
1053 s = file.getvalue()
1054 file.close()
1055 self.assertEqual(s,
1056 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1057 finally:
1058 logging.captureWarnings(False)
Vinay Sajip213faca2008-12-03 23:22:58 +00001059
Vinay Sajip28c382f2010-02-04 18:48:53 +00001060
1061def formatFunc(format, datefmt=None):
1062 return logging.Formatter(format, datefmt)
1063
1064def handlerFunc():
1065 return logging.StreamHandler()
1066
1067class CustomHandler(logging.StreamHandler):
1068 pass
1069
1070class ConfigDictTest(BaseTest):
1071
1072 """Reading logging config from a dictionary."""
1073
1074 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1075
1076 # config0 is a standard configuration.
1077 config0 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001078 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001079 'formatters': {
1080 'form1' : {
1081 'format' : '%(levelname)s ++ %(message)s',
1082 },
1083 },
1084 'handlers' : {
1085 'hand1' : {
1086 'class' : 'logging.StreamHandler',
1087 'formatter' : 'form1',
1088 'level' : 'NOTSET',
1089 'stream' : 'ext://sys.stdout',
1090 },
1091 },
1092 'root' : {
1093 'level' : 'WARNING',
1094 'handlers' : ['hand1'],
1095 },
1096 }
1097
1098 # config1 adds a little to the standard configuration.
1099 config1 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001100 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001101 'formatters': {
1102 'form1' : {
1103 'format' : '%(levelname)s ++ %(message)s',
1104 },
1105 },
1106 'handlers' : {
1107 'hand1' : {
1108 'class' : 'logging.StreamHandler',
1109 'formatter' : 'form1',
1110 'level' : 'NOTSET',
1111 'stream' : 'ext://sys.stdout',
1112 },
1113 },
1114 'loggers' : {
1115 'compiler.parser' : {
1116 'level' : 'DEBUG',
1117 'handlers' : ['hand1'],
1118 },
1119 },
1120 'root' : {
1121 'level' : 'WARNING',
1122 },
1123 }
1124
1125 # config2 has a subtle configuration error that should be reported
1126 config2 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001127 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001128 'formatters': {
1129 'form1' : {
1130 'format' : '%(levelname)s ++ %(message)s',
1131 },
1132 },
1133 'handlers' : {
1134 'hand1' : {
1135 'class' : 'logging.StreamHandler',
1136 'formatter' : 'form1',
1137 'level' : 'NOTSET',
1138 'stream' : 'ext://sys.stdbout',
1139 },
1140 },
1141 'loggers' : {
1142 'compiler.parser' : {
1143 'level' : 'DEBUG',
1144 'handlers' : ['hand1'],
1145 },
1146 },
1147 'root' : {
1148 'level' : 'WARNING',
1149 },
1150 }
1151
1152 #As config1 but with a misspelt level on a handler
1153 config2a = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001154 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001155 'formatters': {
1156 'form1' : {
1157 'format' : '%(levelname)s ++ %(message)s',
1158 },
1159 },
1160 'handlers' : {
1161 'hand1' : {
1162 'class' : 'logging.StreamHandler',
1163 'formatter' : 'form1',
1164 'level' : 'NTOSET',
1165 'stream' : 'ext://sys.stdout',
1166 },
1167 },
1168 'loggers' : {
1169 'compiler.parser' : {
1170 'level' : 'DEBUG',
1171 'handlers' : ['hand1'],
1172 },
1173 },
1174 'root' : {
1175 'level' : 'WARNING',
1176 },
1177 }
1178
1179
1180 #As config1 but with a misspelt level on a logger
1181 config2b = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001182 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001183 'formatters': {
1184 'form1' : {
1185 'format' : '%(levelname)s ++ %(message)s',
1186 },
1187 },
1188 'handlers' : {
1189 'hand1' : {
1190 'class' : 'logging.StreamHandler',
1191 'formatter' : 'form1',
1192 'level' : 'NOTSET',
1193 'stream' : 'ext://sys.stdout',
1194 },
1195 },
1196 'loggers' : {
1197 'compiler.parser' : {
1198 'level' : 'DEBUG',
1199 'handlers' : ['hand1'],
1200 },
1201 },
1202 'root' : {
1203 'level' : 'WRANING',
1204 },
1205 }
1206
1207 # config3 has a less subtle configuration error
1208 config3 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001209 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001210 'formatters': {
1211 'form1' : {
1212 'format' : '%(levelname)s ++ %(message)s',
1213 },
1214 },
1215 'handlers' : {
1216 'hand1' : {
1217 'class' : 'logging.StreamHandler',
1218 'formatter' : 'misspelled_name',
1219 'level' : 'NOTSET',
1220 'stream' : 'ext://sys.stdout',
1221 },
1222 },
1223 'loggers' : {
1224 'compiler.parser' : {
1225 'level' : 'DEBUG',
1226 'handlers' : ['hand1'],
1227 },
1228 },
1229 'root' : {
1230 'level' : 'WARNING',
1231 },
1232 }
1233
1234 # config4 specifies a custom formatter class to be loaded
1235 config4 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001236 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001237 'formatters': {
1238 'form1' : {
1239 '()' : __name__ + '.ExceptionFormatter',
1240 'format' : '%(levelname)s:%(name)s:%(message)s',
1241 },
1242 },
1243 'handlers' : {
1244 'hand1' : {
1245 'class' : 'logging.StreamHandler',
1246 'formatter' : 'form1',
1247 'level' : 'NOTSET',
1248 'stream' : 'ext://sys.stdout',
1249 },
1250 },
1251 'root' : {
1252 'level' : 'NOTSET',
1253 'handlers' : ['hand1'],
1254 },
1255 }
1256
1257 # As config4 but using an actual callable rather than a string
1258 config4a = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001259 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001260 'formatters': {
1261 'form1' : {
1262 '()' : ExceptionFormatter,
1263 'format' : '%(levelname)s:%(name)s:%(message)s',
1264 },
1265 'form2' : {
1266 '()' : __name__ + '.formatFunc',
1267 'format' : '%(levelname)s:%(name)s:%(message)s',
1268 },
1269 'form3' : {
1270 '()' : formatFunc,
1271 'format' : '%(levelname)s:%(name)s:%(message)s',
1272 },
1273 },
1274 'handlers' : {
1275 'hand1' : {
1276 'class' : 'logging.StreamHandler',
1277 'formatter' : 'form1',
1278 'level' : 'NOTSET',
1279 'stream' : 'ext://sys.stdout',
1280 },
1281 'hand2' : {
1282 '()' : handlerFunc,
1283 },
1284 },
1285 'root' : {
1286 'level' : 'NOTSET',
1287 'handlers' : ['hand1'],
1288 },
1289 }
1290
1291 # config5 specifies a custom handler class to be loaded
1292 config5 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001293 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001294 'formatters': {
1295 'form1' : {
1296 'format' : '%(levelname)s ++ %(message)s',
1297 },
1298 },
1299 'handlers' : {
1300 'hand1' : {
1301 'class' : __name__ + '.CustomHandler',
1302 'formatter' : 'form1',
1303 'level' : 'NOTSET',
1304 'stream' : 'ext://sys.stdout',
1305 },
1306 },
1307 'loggers' : {
1308 'compiler.parser' : {
1309 'level' : 'DEBUG',
1310 'handlers' : ['hand1'],
1311 },
1312 },
1313 'root' : {
1314 'level' : 'WARNING',
1315 },
1316 }
1317
1318 # config6 specifies a custom handler class to be loaded
1319 # but has bad arguments
1320 config6 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001321 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001322 'formatters': {
1323 'form1' : {
1324 'format' : '%(levelname)s ++ %(message)s',
1325 },
1326 },
1327 'handlers' : {
1328 'hand1' : {
1329 'class' : __name__ + '.CustomHandler',
1330 'formatter' : 'form1',
1331 'level' : 'NOTSET',
1332 'stream' : 'ext://sys.stdout',
1333 '9' : 'invalid parameter name',
1334 },
1335 },
1336 'loggers' : {
1337 'compiler.parser' : {
1338 'level' : 'DEBUG',
1339 'handlers' : ['hand1'],
1340 },
1341 },
1342 'root' : {
1343 'level' : 'WARNING',
1344 },
1345 }
1346
1347 #config 7 does not define compiler.parser but defines compiler.lexer
1348 #so compiler.parser should be disabled after applying it
1349 config7 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001350 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001351 'formatters': {
1352 'form1' : {
1353 'format' : '%(levelname)s ++ %(message)s',
1354 },
1355 },
1356 'handlers' : {
1357 'hand1' : {
1358 'class' : 'logging.StreamHandler',
1359 'formatter' : 'form1',
1360 'level' : 'NOTSET',
1361 'stream' : 'ext://sys.stdout',
1362 },
1363 },
1364 'loggers' : {
1365 'compiler.lexer' : {
1366 'level' : 'DEBUG',
1367 'handlers' : ['hand1'],
1368 },
1369 },
1370 'root' : {
1371 'level' : 'WARNING',
1372 },
1373 }
1374
1375 config8 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001376 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001377 'disable_existing_loggers' : False,
1378 'formatters': {
1379 'form1' : {
1380 'format' : '%(levelname)s ++ %(message)s',
1381 },
1382 },
1383 'handlers' : {
1384 'hand1' : {
1385 'class' : 'logging.StreamHandler',
1386 'formatter' : 'form1',
1387 'level' : 'NOTSET',
1388 'stream' : 'ext://sys.stdout',
1389 },
1390 },
1391 'loggers' : {
1392 'compiler' : {
1393 'level' : 'DEBUG',
1394 'handlers' : ['hand1'],
1395 },
1396 'compiler.lexer' : {
1397 },
1398 },
1399 'root' : {
1400 'level' : 'WARNING',
1401 },
1402 }
1403
1404 config9 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001405 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001406 'formatters': {
1407 'form1' : {
1408 'format' : '%(levelname)s ++ %(message)s',
1409 },
1410 },
1411 'handlers' : {
1412 'hand1' : {
1413 'class' : 'logging.StreamHandler',
1414 'formatter' : 'form1',
1415 'level' : 'WARNING',
1416 'stream' : 'ext://sys.stdout',
1417 },
1418 },
1419 'loggers' : {
1420 'compiler.parser' : {
1421 'level' : 'WARNING',
1422 'handlers' : ['hand1'],
1423 },
1424 },
1425 'root' : {
1426 'level' : 'NOTSET',
1427 },
1428 }
1429
1430 config9a = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001431 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001432 'incremental' : True,
1433 'handlers' : {
1434 'hand1' : {
1435 'level' : 'WARNING',
1436 },
1437 },
1438 'loggers' : {
1439 'compiler.parser' : {
1440 'level' : 'INFO',
1441 },
1442 },
1443 }
1444
1445 config9b = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001446 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001447 'incremental' : True,
1448 'handlers' : {
1449 'hand1' : {
1450 'level' : 'INFO',
1451 },
1452 },
1453 'loggers' : {
1454 'compiler.parser' : {
1455 'level' : 'INFO',
1456 },
1457 },
1458 }
1459
1460 #As config1 but with a filter added
1461 config10 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001462 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001463 'formatters': {
1464 'form1' : {
1465 'format' : '%(levelname)s ++ %(message)s',
1466 },
1467 },
1468 'filters' : {
1469 'filt1' : {
1470 'name' : 'compiler.parser',
1471 },
1472 },
1473 'handlers' : {
1474 'hand1' : {
1475 'class' : 'logging.StreamHandler',
1476 'formatter' : 'form1',
1477 'level' : 'NOTSET',
1478 'stream' : 'ext://sys.stdout',
1479 'filters' : ['filt1'],
1480 },
1481 },
1482 'loggers' : {
1483 'compiler.parser' : {
1484 'level' : 'DEBUG',
1485 'filters' : ['filt1'],
1486 },
1487 },
1488 'root' : {
1489 'level' : 'WARNING',
1490 'handlers' : ['hand1'],
1491 },
1492 }
1493
Vinay Sajipf7610b22010-02-04 21:40:56 +00001494 #As config1 but using cfg:// references
1495 config11 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001496 'version': 1,
1497 'true_formatters': {
1498 'form1' : {
1499 'format' : '%(levelname)s ++ %(message)s',
1500 },
1501 },
1502 'handler_configs': {
1503 'hand1' : {
1504 'class' : 'logging.StreamHandler',
1505 'formatter' : 'form1',
1506 'level' : 'NOTSET',
1507 'stream' : 'ext://sys.stdout',
1508 },
1509 },
1510 'formatters' : 'cfg://true_formatters',
1511 'handlers' : {
1512 'hand1' : 'cfg://handler_configs[hand1]',
1513 },
1514 'loggers' : {
1515 'compiler.parser' : {
1516 'level' : 'DEBUG',
1517 'handlers' : ['hand1'],
1518 },
1519 },
1520 'root' : {
1521 'level' : 'WARNING',
1522 },
1523 }
1524
1525 #As config11 but missing the version key
1526 config12 = {
1527 'true_formatters': {
1528 'form1' : {
1529 'format' : '%(levelname)s ++ %(message)s',
1530 },
1531 },
1532 'handler_configs': {
1533 'hand1' : {
1534 'class' : 'logging.StreamHandler',
1535 'formatter' : 'form1',
1536 'level' : 'NOTSET',
1537 'stream' : 'ext://sys.stdout',
1538 },
1539 },
1540 'formatters' : 'cfg://true_formatters',
1541 'handlers' : {
1542 'hand1' : 'cfg://handler_configs[hand1]',
1543 },
1544 'loggers' : {
1545 'compiler.parser' : {
1546 'level' : 'DEBUG',
1547 'handlers' : ['hand1'],
1548 },
1549 },
1550 'root' : {
1551 'level' : 'WARNING',
1552 },
1553 }
1554
1555 #As config11 but using an unsupported version
1556 config13 = {
1557 'version': 2,
Vinay Sajipf7610b22010-02-04 21:40:56 +00001558 'true_formatters': {
1559 'form1' : {
1560 'format' : '%(levelname)s ++ %(message)s',
1561 },
1562 },
1563 'handler_configs': {
1564 'hand1' : {
1565 'class' : 'logging.StreamHandler',
1566 'formatter' : 'form1',
1567 'level' : 'NOTSET',
1568 'stream' : 'ext://sys.stdout',
1569 },
1570 },
1571 'formatters' : 'cfg://true_formatters',
1572 'handlers' : {
1573 'hand1' : 'cfg://handler_configs[hand1]',
1574 },
1575 'loggers' : {
1576 'compiler.parser' : {
1577 'level' : 'DEBUG',
1578 'handlers' : ['hand1'],
1579 },
1580 },
1581 'root' : {
1582 'level' : 'WARNING',
1583 },
1584 }
1585
Vinay Sajip28c382f2010-02-04 18:48:53 +00001586 def apply_config(self, conf):
1587 logging.config.dictConfig(conf)
1588
1589 def test_config0_ok(self):
1590 # A simple config which overrides the default settings.
1591 with captured_stdout() as output:
1592 self.apply_config(self.config0)
1593 logger = logging.getLogger()
1594 # Won't output anything
1595 logger.info(self.next_message())
1596 # Outputs a message
1597 logger.error(self.next_message())
1598 self.assert_log_lines([
1599 ('ERROR', '2'),
1600 ], stream=output)
1601 # Original logger output is empty.
1602 self.assert_log_lines([])
1603
1604 def test_config1_ok(self, config=config1):
1605 # A config defining a sub-parser as well.
1606 with captured_stdout() as output:
1607 self.apply_config(config)
1608 logger = logging.getLogger("compiler.parser")
1609 # Both will output a message
1610 logger.info(self.next_message())
1611 logger.error(self.next_message())
1612 self.assert_log_lines([
1613 ('INFO', '1'),
1614 ('ERROR', '2'),
1615 ], stream=output)
1616 # Original logger output is empty.
1617 self.assert_log_lines([])
1618
1619 def test_config2_failure(self):
1620 # A simple config which overrides the default settings.
1621 self.assertRaises(StandardError, self.apply_config, self.config2)
1622
1623 def test_config2a_failure(self):
1624 # A simple config which overrides the default settings.
1625 self.assertRaises(StandardError, self.apply_config, self.config2a)
1626
1627 def test_config2b_failure(self):
1628 # A simple config which overrides the default settings.
1629 self.assertRaises(StandardError, self.apply_config, self.config2b)
1630
1631 def test_config3_failure(self):
1632 # A simple config which overrides the default settings.
1633 self.assertRaises(StandardError, self.apply_config, self.config3)
1634
1635 def test_config4_ok(self):
1636 # A config specifying a custom formatter class.
1637 with captured_stdout() as output:
1638 self.apply_config(self.config4)
1639 #logger = logging.getLogger()
1640 try:
1641 raise RuntimeError()
1642 except RuntimeError:
1643 logging.exception("just testing")
1644 sys.stdout.seek(0)
Ezio Melotti2623a372010-11-21 13:34:58 +00001645 self.assertEqual(output.getvalue(),
Vinay Sajip28c382f2010-02-04 18:48:53 +00001646 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1647 # Original logger output is empty
1648 self.assert_log_lines([])
1649
1650 def test_config4a_ok(self):
1651 # A config specifying a custom formatter class.
1652 with captured_stdout() as output:
1653 self.apply_config(self.config4a)
1654 #logger = logging.getLogger()
1655 try:
1656 raise RuntimeError()
1657 except RuntimeError:
1658 logging.exception("just testing")
1659 sys.stdout.seek(0)
Ezio Melotti2623a372010-11-21 13:34:58 +00001660 self.assertEqual(output.getvalue(),
Vinay Sajip28c382f2010-02-04 18:48:53 +00001661 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1662 # Original logger output is empty
1663 self.assert_log_lines([])
1664
1665 def test_config5_ok(self):
1666 self.test_config1_ok(config=self.config5)
1667
1668 def test_config6_failure(self):
1669 self.assertRaises(StandardError, self.apply_config, self.config6)
1670
1671 def test_config7_ok(self):
1672 with captured_stdout() as output:
1673 self.apply_config(self.config1)
1674 logger = logging.getLogger("compiler.parser")
1675 # Both will output a message
1676 logger.info(self.next_message())
1677 logger.error(self.next_message())
1678 self.assert_log_lines([
1679 ('INFO', '1'),
1680 ('ERROR', '2'),
1681 ], stream=output)
1682 # Original logger output is empty.
1683 self.assert_log_lines([])
1684 with captured_stdout() as output:
1685 self.apply_config(self.config7)
1686 logger = logging.getLogger("compiler.parser")
1687 self.assertTrue(logger.disabled)
1688 logger = logging.getLogger("compiler.lexer")
1689 # Both will output a message
1690 logger.info(self.next_message())
1691 logger.error(self.next_message())
1692 self.assert_log_lines([
1693 ('INFO', '3'),
1694 ('ERROR', '4'),
1695 ], stream=output)
1696 # Original logger output is empty.
1697 self.assert_log_lines([])
1698
1699 #Same as test_config_7_ok but don't disable old loggers.
1700 def test_config_8_ok(self):
1701 with captured_stdout() as output:
1702 self.apply_config(self.config1)
1703 logger = logging.getLogger("compiler.parser")
1704 # Both will output a message
1705 logger.info(self.next_message())
1706 logger.error(self.next_message())
1707 self.assert_log_lines([
1708 ('INFO', '1'),
1709 ('ERROR', '2'),
1710 ], stream=output)
1711 # Original logger output is empty.
1712 self.assert_log_lines([])
1713 with captured_stdout() as output:
1714 self.apply_config(self.config8)
1715 logger = logging.getLogger("compiler.parser")
1716 self.assertFalse(logger.disabled)
1717 # Both will output a message
1718 logger.info(self.next_message())
1719 logger.error(self.next_message())
1720 logger = logging.getLogger("compiler.lexer")
1721 # Both will output a message
1722 logger.info(self.next_message())
1723 logger.error(self.next_message())
1724 self.assert_log_lines([
1725 ('INFO', '3'),
1726 ('ERROR', '4'),
1727 ('INFO', '5'),
1728 ('ERROR', '6'),
1729 ], stream=output)
1730 # Original logger output is empty.
1731 self.assert_log_lines([])
1732
1733 def test_config_9_ok(self):
1734 with captured_stdout() as output:
1735 self.apply_config(self.config9)
1736 logger = logging.getLogger("compiler.parser")
1737 #Nothing will be output since both handler and logger are set to WARNING
1738 logger.info(self.next_message())
1739 self.assert_log_lines([], stream=output)
1740 self.apply_config(self.config9a)
1741 #Nothing will be output since both handler is still set to WARNING
1742 logger.info(self.next_message())
1743 self.assert_log_lines([], stream=output)
1744 self.apply_config(self.config9b)
1745 #Message should now be output
1746 logger.info(self.next_message())
1747 self.assert_log_lines([
1748 ('INFO', '3'),
1749 ], stream=output)
1750
1751 def test_config_10_ok(self):
1752 with captured_stdout() as output:
1753 self.apply_config(self.config10)
1754 logger = logging.getLogger("compiler.parser")
1755 logger.warning(self.next_message())
1756 logger = logging.getLogger('compiler')
1757 #Not output, because filtered
1758 logger.warning(self.next_message())
1759 logger = logging.getLogger('compiler.lexer')
1760 #Not output, because filtered
1761 logger.warning(self.next_message())
1762 logger = logging.getLogger("compiler.parser.codegen")
1763 #Output, as not filtered
1764 logger.error(self.next_message())
1765 self.assert_log_lines([
1766 ('WARNING', '1'),
1767 ('ERROR', '4'),
1768 ], stream=output)
1769
Vinay Sajipf7610b22010-02-04 21:40:56 +00001770 def test_config11_ok(self):
1771 self.test_config1_ok(self.config11)
1772
Vinay Sajipd45a2782010-03-06 15:12:08 +00001773 def test_config12_failure(self):
1774 self.assertRaises(StandardError, self.apply_config, self.config12)
1775
1776 def test_config13_failure(self):
1777 self.assertRaises(StandardError, self.apply_config, self.config13)
1778
Victor Stinner6a102812010-04-27 23:55:59 +00001779 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajip28c382f2010-02-04 18:48:53 +00001780 def setup_via_listener(self, text):
Vinay Sajip27a13702010-05-03 15:11:53 +00001781 # Ask for a randomly assigned port (by using port 0)
1782 t = logging.config.listen(0)
Vinay Sajip28c382f2010-02-04 18:48:53 +00001783 t.start()
Benjamin Peterson239f1382010-02-06 22:08:15 +00001784 t.ready.wait()
Vinay Sajip27a13702010-05-03 15:11:53 +00001785 # Now get the port allocated
1786 port = t.port
Vinay Sajip9a164ac2010-02-08 16:05:50 +00001787 t.ready.clear()
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001788 try:
1789 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Vinay Sajip23739262010-02-05 23:43:11 +00001790 sock.settimeout(2.0)
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001791 sock.connect(('localhost', port))
Vinay Sajip28c382f2010-02-04 18:48:53 +00001792
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001793 slen = struct.pack('>L', len(text))
1794 s = slen + text
1795 sentsofar = 0
1796 left = len(s)
1797 while left > 0:
1798 sent = sock.send(s[sentsofar:])
1799 sentsofar += sent
1800 left -= sent
1801 sock.close()
1802 finally:
Vinay Sajip9a164ac2010-02-08 16:05:50 +00001803 t.ready.wait(2.0)
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001804 logging.config.stopListening()
Vinay Sajip23739262010-02-05 23:43:11 +00001805 t.join(2.0)
Vinay Sajip28c382f2010-02-04 18:48:53 +00001806
Vinay Sajip23739262010-02-05 23:43:11 +00001807 def test_listen_config_10_ok(self):
Vinay Sajip28c382f2010-02-04 18:48:53 +00001808 with captured_stdout() as output:
1809 self.setup_via_listener(json.dumps(self.config10))
1810 logger = logging.getLogger("compiler.parser")
1811 logger.warning(self.next_message())
1812 logger = logging.getLogger('compiler')
1813 #Not output, because filtered
1814 logger.warning(self.next_message())
1815 logger = logging.getLogger('compiler.lexer')
1816 #Not output, because filtered
1817 logger.warning(self.next_message())
1818 logger = logging.getLogger("compiler.parser.codegen")
1819 #Output, as not filtered
1820 logger.error(self.next_message())
1821 self.assert_log_lines([
1822 ('WARNING', '1'),
1823 ('ERROR', '4'),
1824 ], stream=output)
1825
Vinay Sajip23739262010-02-05 23:43:11 +00001826 def test_listen_config_1_ok(self):
Vinay Sajip28c382f2010-02-04 18:48:53 +00001827 with captured_stdout() as output:
1828 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
1829 logger = logging.getLogger("compiler.parser")
1830 # Both will output a message
1831 logger.info(self.next_message())
1832 logger.error(self.next_message())
1833 self.assert_log_lines([
1834 ('INFO', '1'),
1835 ('ERROR', '2'),
1836 ], stream=output)
1837 # Original logger output is empty.
1838 self.assert_log_lines([])
1839
1840
Vinay Sajip657514a2010-02-07 01:37:08 +00001841class ManagerTest(BaseTest):
1842 def test_manager_loggerclass(self):
1843 logged = []
1844
1845 class MyLogger(logging.Logger):
1846 def _log(self, level, msg, args, exc_info=None, extra=None):
1847 logged.append(msg)
1848
1849 man = logging.Manager(None)
1850 self.assertRaises(TypeError, man.setLoggerClass, int)
1851 man.setLoggerClass(MyLogger)
1852 logger = man.getLogger('test')
Vinay Sajip657514a2010-02-07 01:37:08 +00001853 logger.warning('should appear in logged')
1854 logging.warning('should not appear in logged')
1855
1856 self.assertEqual(logged, ['should appear in logged'])
1857
1858
Vinay Sajip804899b2010-03-22 15:29:01 +00001859class ChildLoggerTest(BaseTest):
1860 def test_child_loggers(self):
1861 r = logging.getLogger()
1862 l1 = logging.getLogger('abc')
1863 l2 = logging.getLogger('def.ghi')
1864 c1 = r.getChild('xyz')
1865 c2 = r.getChild('uvw.xyz')
1866 self.assertTrue(c1 is logging.getLogger('xyz'))
1867 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
1868 c1 = l1.getChild('def')
1869 c2 = c1.getChild('ghi')
1870 c3 = l1.getChild('def.ghi')
1871 self.assertTrue(c1 is logging.getLogger('abc.def'))
1872 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
1873 self.assertTrue(c2 is c3)
1874
1875
Brett Cannon56c4deb2008-03-03 00:38:58 +00001876# Set the locale to the platform-dependent default. I have no idea
1877# why the test does this, but in any case we save the current locale
1878# first and restore it at the end.
1879@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00001880def test_main():
Brett Cannon56c4deb2008-03-03 00:38:58 +00001881 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip657514a2010-02-07 01:37:08 +00001882 CustomLevelsAndFiltersTest, MemoryHandlerTest,
1883 ConfigFileTest, SocketHandlerTest, MemoryTest,
Vinay Sajip804899b2010-03-22 15:29:01 +00001884 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
1885 ChildLoggerTest)
Jeremy Hylton096d9862003-07-18 03:19:20 +00001886
Brett Cannon56c4deb2008-03-03 00:38:58 +00001887if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001888 test_main()