blob: 8cbc060bf627ebb8de43de19d1a7ab5351f41e93 [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 copy
30import cPickle
31import cStringIO
32import gc
Vinay Sajip28c382f2010-02-04 18:48:53 +000033import json
Brett Cannon56c4deb2008-03-03 00:38:58 +000034import os
35import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000036import select
Brett Cannon56c4deb2008-03-03 00:38:58 +000037import socket
Georg Brandle152a772008-05-24 18:31:28 +000038from SocketServer import ThreadingTCPServer, StreamRequestHandler
Brett Cannon56c4deb2008-03-03 00:38:58 +000039import string
40import struct
41import sys
42import tempfile
Vinay Sajip3dd734f2010-02-05 14:52:05 +000043from test.test_support import captured_stdout, run_with_locale, run_unittest,\
44 find_unused_port
Brett Cannon56c4deb2008-03-03 00:38:58 +000045import textwrap
46import threading
47import time
48import types
49import unittest
Vinay Sajip213faca2008-12-03 23:22:58 +000050import warnings
Brett Cannon56c4deb2008-03-03 00:38:58 +000051import weakref
Brett Cannonf9db8a32008-02-17 01:59:18 +000052
53
Brett Cannon56c4deb2008-03-03 00:38:58 +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
Brett Cannonf9db8a32008-02-17 01:59:18 +000066 logging._acquireLock()
67 try:
Brett Cannon56c4deb2008-03-03 00:38:58 +000068 self.saved_handlers = logging._handlers.copy()
69 self.saved_handler_list = logging._handlerList[:]
70 self.saved_loggers = logger_dict.copy()
71 self.saved_level_names = logging._levelNames.copy()
Brett Cannonf9db8a32008-02-17 01:59:18 +000072 finally:
73 logging._releaseLock()
74
Brett Cannon56c4deb2008-03-03 00:38:58 +000075 self.root_logger = logging.getLogger("")
76 self.original_logging_level = self.root_logger.getEffectiveLevel()
77
78 self.stream = cStringIO.StringIO()
79 self.root_logger.setLevel(logging.DEBUG)
80 self.root_hdlr = logging.StreamHandler(self.stream)
81 self.root_formatter = logging.Formatter(self.log_format)
82 self.root_hdlr.setFormatter(self.root_formatter)
83 self.root_logger.addHandler(self.root_hdlr)
84
85 def tearDown(self):
86 """Remove our logging stream, and restore the original logging
87 level."""
88 self.stream.close()
89 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajip28c382f2010-02-04 18:48:53 +000090 while self.root_logger.handlers:
91 h = self.root_logger.handlers[0]
92 self.root_logger.removeHandler(h)
93 h.close()
Brett Cannon56c4deb2008-03-03 00:38:58 +000094 self.root_logger.setLevel(self.original_logging_level)
95 logging._acquireLock()
96 try:
97 logging._levelNames.clear()
98 logging._levelNames.update(self.saved_level_names)
99 logging._handlers.clear()
100 logging._handlers.update(self.saved_handlers)
101 logging._handlerList[:] = self.saved_handler_list
102 loggerDict = logging.getLogger().manager.loggerDict
103 loggerDict.clear()
104 loggerDict.update(self.saved_loggers)
105 finally:
106 logging._releaseLock()
107
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000108 def assert_log_lines(self, expected_values, stream=None):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000109 """Match the collected log lines against the regular expression
110 self.expected_log_pat, and compare the extracted group values to
111 the expected_values list of tuples."""
112 stream = stream or self.stream
113 pat = re.compile(self.expected_log_pat)
114 try:
115 stream.reset()
116 actual_lines = stream.readlines()
117 except AttributeError:
118 # StringIO.StringIO lacks a reset() method.
119 actual_lines = stream.getvalue().splitlines()
120 self.assertEquals(len(actual_lines), len(expected_values))
121 for actual, expected in zip(actual_lines, expected_values):
122 match = pat.search(actual)
123 if not match:
124 self.fail("Log line does not match expected pattern:\n" +
125 actual)
126 self.assertEquals(tuple(match.groups()), expected)
127 s = stream.read()
128 if s:
129 self.fail("Remaining output at end of log stream:\n" + s)
130
131 def next_message(self):
132 """Generate a message consisting solely of an auto-incrementing
133 integer."""
134 self.message_num += 1
135 return "%d" % self.message_num
136
137
138class BuiltinLevelsTest(BaseTest):
139 """Test builtin levels and their inheritance."""
140
141 def test_flat(self):
142 #Logging levels in a flat logger namespace.
143 m = self.next_message
144
145 ERR = logging.getLogger("ERR")
146 ERR.setLevel(logging.ERROR)
147 INF = logging.getLogger("INF")
148 INF.setLevel(logging.INFO)
149 DEB = logging.getLogger("DEB")
150 DEB.setLevel(logging.DEBUG)
151
152 # These should log.
153 ERR.log(logging.CRITICAL, m())
154 ERR.error(m())
155
156 INF.log(logging.CRITICAL, m())
157 INF.error(m())
158 INF.warn(m())
159 INF.info(m())
160
161 DEB.log(logging.CRITICAL, m())
162 DEB.error(m())
163 DEB.warn (m())
164 DEB.info (m())
165 DEB.debug(m())
166
167 # These should not log.
168 ERR.warn(m())
169 ERR.info(m())
170 ERR.debug(m())
171
172 INF.debug(m())
173
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000174 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000175 ('ERR', 'CRITICAL', '1'),
176 ('ERR', 'ERROR', '2'),
177 ('INF', 'CRITICAL', '3'),
178 ('INF', 'ERROR', '4'),
179 ('INF', 'WARNING', '5'),
180 ('INF', 'INFO', '6'),
181 ('DEB', 'CRITICAL', '7'),
182 ('DEB', 'ERROR', '8'),
183 ('DEB', 'WARNING', '9'),
184 ('DEB', 'INFO', '10'),
185 ('DEB', 'DEBUG', '11'),
186 ])
187
188 def test_nested_explicit(self):
189 # Logging levels in a nested namespace, all explicitly set.
190 m = self.next_message
191
192 INF = logging.getLogger("INF")
193 INF.setLevel(logging.INFO)
194 INF_ERR = logging.getLogger("INF.ERR")
195 INF_ERR.setLevel(logging.ERROR)
196
197 # These should log.
198 INF_ERR.log(logging.CRITICAL, m())
199 INF_ERR.error(m())
200
201 # These should not log.
202 INF_ERR.warn(m())
203 INF_ERR.info(m())
204 INF_ERR.debug(m())
205
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000206 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000207 ('INF.ERR', 'CRITICAL', '1'),
208 ('INF.ERR', 'ERROR', '2'),
209 ])
210
211 def test_nested_inherited(self):
212 #Logging levels in a nested namespace, inherited from parent loggers.
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 INF_UNDEF = logging.getLogger("INF.UNDEF")
220 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
221 UNDEF = logging.getLogger("UNDEF")
222
223 # These should log.
224 INF_UNDEF.log(logging.CRITICAL, m())
225 INF_UNDEF.error(m())
226 INF_UNDEF.warn(m())
227 INF_UNDEF.info(m())
228 INF_ERR_UNDEF.log(logging.CRITICAL, m())
229 INF_ERR_UNDEF.error(m())
230
231 # These should not log.
232 INF_UNDEF.debug(m())
233 INF_ERR_UNDEF.warn(m())
234 INF_ERR_UNDEF.info(m())
235 INF_ERR_UNDEF.debug(m())
236
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000237 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000238 ('INF.UNDEF', 'CRITICAL', '1'),
239 ('INF.UNDEF', 'ERROR', '2'),
240 ('INF.UNDEF', 'WARNING', '3'),
241 ('INF.UNDEF', 'INFO', '4'),
242 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
243 ('INF.ERR.UNDEF', 'ERROR', '6'),
244 ])
245
246 def test_nested_with_virtual_parent(self):
247 # Logging levels when some parent does not exist yet.
248 m = self.next_message
249
250 INF = logging.getLogger("INF")
251 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
252 CHILD = logging.getLogger("INF.BADPARENT")
253 INF.setLevel(logging.INFO)
254
255 # These should log.
256 GRANDCHILD.log(logging.FATAL, m())
257 GRANDCHILD.info(m())
258 CHILD.log(logging.FATAL, m())
259 CHILD.info(m())
260
261 # These should not log.
262 GRANDCHILD.debug(m())
263 CHILD.debug(m())
264
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000265 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000266 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
267 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
268 ('INF.BADPARENT', 'CRITICAL', '3'),
269 ('INF.BADPARENT', 'INFO', '4'),
270 ])
271
272
273class BasicFilterTest(BaseTest):
274
275 """Test the bundled Filter class."""
276
277 def test_filter(self):
278 # Only messages satisfying the specified criteria pass through the
279 # filter.
280 filter_ = logging.Filter("spam.eggs")
281 handler = self.root_logger.handlers[0]
282 try:
283 handler.addFilter(filter_)
284 spam = logging.getLogger("spam")
285 spam_eggs = logging.getLogger("spam.eggs")
286 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
287 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
288
289 spam.info(self.next_message())
290 spam_eggs.info(self.next_message()) # Good.
291 spam_eggs_fish.info(self.next_message()) # Good.
292 spam_bakedbeans.info(self.next_message())
293
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000294 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000295 ('spam.eggs', 'INFO', '2'),
296 ('spam.eggs.fish', 'INFO', '3'),
297 ])
298 finally:
299 handler.removeFilter(filter_)
300
301
302#
303# First, we define our levels. There can be as many as you want - the only
304# limitations are that they should be integers, the lowest should be > 0 and
305# larger values mean less information being logged. If you need specific
306# level values which do not fit into these limitations, you can use a
307# mapping dictionary to convert between your application levels and the
308# logging system.
309#
310SILENT = 120
311TACITURN = 119
312TERSE = 118
313EFFUSIVE = 117
314SOCIABLE = 116
315VERBOSE = 115
316TALKATIVE = 114
317GARRULOUS = 113
318CHATTERBOX = 112
319BORING = 111
320
321LEVEL_RANGE = range(BORING, SILENT + 1)
322
323#
324# Next, we define names for our levels. You don't need to do this - in which
325# case the system will use "Level n" to denote the text for the level.
326#
327my_logging_levels = {
328 SILENT : 'Silent',
329 TACITURN : 'Taciturn',
330 TERSE : 'Terse',
331 EFFUSIVE : 'Effusive',
332 SOCIABLE : 'Sociable',
333 VERBOSE : 'Verbose',
334 TALKATIVE : 'Talkative',
335 GARRULOUS : 'Garrulous',
336 CHATTERBOX : 'Chatterbox',
337 BORING : 'Boring',
338}
339
340class GarrulousFilter(logging.Filter):
341
342 """A filter which blocks garrulous messages."""
343
344 def filter(self, record):
345 return record.levelno != GARRULOUS
346
347class VerySpecificFilter(logging.Filter):
348
349 """A filter which blocks sociable and taciturn messages."""
350
351 def filter(self, record):
352 return record.levelno not in [SOCIABLE, TACITURN]
353
354
355class CustomLevelsAndFiltersTest(BaseTest):
356
357 """Test various filtering possibilities with custom logging levels."""
358
359 # Skip the logger name group.
360 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
361
362 def setUp(self):
363 BaseTest.setUp(self)
364 for k, v in my_logging_levels.items():
365 logging.addLevelName(k, v)
366
367 def log_at_all_levels(self, logger):
368 for lvl in LEVEL_RANGE:
369 logger.log(lvl, self.next_message())
370
371 def test_logger_filter(self):
372 # Filter at logger level.
373 self.root_logger.setLevel(VERBOSE)
374 # Levels >= 'Verbose' are good.
375 self.log_at_all_levels(self.root_logger)
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000376 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000377 ('Verbose', '5'),
378 ('Sociable', '6'),
379 ('Effusive', '7'),
380 ('Terse', '8'),
381 ('Taciturn', '9'),
382 ('Silent', '10'),
383 ])
384
385 def test_handler_filter(self):
386 # Filter at handler level.
387 self.root_logger.handlers[0].setLevel(SOCIABLE)
388 try:
389 # Levels >= 'Sociable' are good.
390 self.log_at_all_levels(self.root_logger)
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000391 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000392 ('Sociable', '6'),
393 ('Effusive', '7'),
394 ('Terse', '8'),
395 ('Taciturn', '9'),
396 ('Silent', '10'),
397 ])
398 finally:
399 self.root_logger.handlers[0].setLevel(logging.NOTSET)
400
401 def test_specific_filters(self):
402 # Set a specific filter object on the handler, and then add another
403 # filter object on the logger itself.
404 handler = self.root_logger.handlers[0]
405 specific_filter = None
406 garr = GarrulousFilter()
407 handler.addFilter(garr)
408 try:
409 self.log_at_all_levels(self.root_logger)
410 first_lines = [
411 # Notice how 'Garrulous' is missing
412 ('Boring', '1'),
413 ('Chatterbox', '2'),
414 ('Talkative', '4'),
415 ('Verbose', '5'),
416 ('Sociable', '6'),
417 ('Effusive', '7'),
418 ('Terse', '8'),
419 ('Taciturn', '9'),
420 ('Silent', '10'),
421 ]
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000422 self.assert_log_lines(first_lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000423
424 specific_filter = VerySpecificFilter()
425 self.root_logger.addFilter(specific_filter)
426 self.log_at_all_levels(self.root_logger)
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000427 self.assert_log_lines(first_lines + [
Brett Cannon56c4deb2008-03-03 00:38:58 +0000428 # Not only 'Garrulous' is still missing, but also 'Sociable'
429 # and 'Taciturn'
430 ('Boring', '11'),
431 ('Chatterbox', '12'),
432 ('Talkative', '14'),
433 ('Verbose', '15'),
434 ('Effusive', '17'),
435 ('Terse', '18'),
436 ('Silent', '20'),
437 ])
438 finally:
439 if specific_filter:
440 self.root_logger.removeFilter(specific_filter)
441 handler.removeFilter(garr)
442
443
444class MemoryHandlerTest(BaseTest):
445
446 """Tests for the MemoryHandler."""
447
448 # Do not bother with a logger name group.
449 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
450
451 def setUp(self):
452 BaseTest.setUp(self)
453 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
454 self.root_hdlr)
455 self.mem_logger = logging.getLogger('mem')
456 self.mem_logger.propagate = 0
457 self.mem_logger.addHandler(self.mem_hdlr)
458
459 def tearDown(self):
460 self.mem_hdlr.close()
Amaury Forgeot d'Arc2aece572008-03-29 01:42:31 +0000461 BaseTest.tearDown(self)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000462
463 def test_flush(self):
464 # The memory handler flushes to its target handler based on specific
465 # criteria (message count and message level).
466 self.mem_logger.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000467 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000468 self.mem_logger.info(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000469 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000470 # This will flush because the level is >= logging.WARNING
471 self.mem_logger.warn(self.next_message())
472 lines = [
473 ('DEBUG', '1'),
474 ('INFO', '2'),
475 ('WARNING', '3'),
476 ]
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000477 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000478 for n in (4, 14):
479 for i in range(9):
480 self.mem_logger.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000481 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000482 # This will flush because it's the 10th message since the last
483 # flush.
484 self.mem_logger.debug(self.next_message())
485 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000486 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000487
488 self.mem_logger.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000489 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000490
491
492class ExceptionFormatter(logging.Formatter):
493 """A special exception formatter."""
494 def formatException(self, ei):
495 return "Got a [%s]" % ei[0].__name__
496
497
498class ConfigFileTest(BaseTest):
499
500 """Reading logging config from a .ini-style config file."""
501
502 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
503
504 # config0 is a standard configuration.
505 config0 = """
506 [loggers]
507 keys=root
508
509 [handlers]
510 keys=hand1
511
512 [formatters]
513 keys=form1
514
515 [logger_root]
516 level=WARNING
517 handlers=hand1
518
519 [handler_hand1]
520 class=StreamHandler
521 level=NOTSET
522 formatter=form1
523 args=(sys.stdout,)
524
525 [formatter_form1]
526 format=%(levelname)s ++ %(message)s
527 datefmt=
528 """
529
530 # config1 adds a little to the standard configuration.
531 config1 = """
532 [loggers]
533 keys=root,parser
534
535 [handlers]
536 keys=hand1
537
538 [formatters]
539 keys=form1
540
541 [logger_root]
542 level=WARNING
543 handlers=
544
545 [logger_parser]
546 level=DEBUG
547 handlers=hand1
548 propagate=1
549 qualname=compiler.parser
550
551 [handler_hand1]
552 class=StreamHandler
553 level=NOTSET
554 formatter=form1
555 args=(sys.stdout,)
556
557 [formatter_form1]
558 format=%(levelname)s ++ %(message)s
559 datefmt=
560 """
561
562 # config2 has a subtle configuration error that should be reported
563 config2 = config1.replace("sys.stdout", "sys.stbout")
564
565 # config3 has a less subtle configuration error
566 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
567
568 # config4 specifies a custom formatter class to be loaded
569 config4 = """
570 [loggers]
571 keys=root
572
573 [handlers]
574 keys=hand1
575
576 [formatters]
577 keys=form1
578
579 [logger_root]
580 level=NOTSET
581 handlers=hand1
582
583 [handler_hand1]
584 class=StreamHandler
585 level=NOTSET
586 formatter=form1
587 args=(sys.stdout,)
588
589 [formatter_form1]
590 class=""" + __name__ + """.ExceptionFormatter
591 format=%(levelname)s:%(name)s:%(message)s
592 datefmt=
593 """
594
Vinay Sajip70fdc952008-07-18 09:00:00 +0000595 # config5 specifies a custom handler class to be loaded
596 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
597
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000598 # config6 uses ', ' delimiters in the handlers and formatters sections
599 config6 = """
600 [loggers]
601 keys=root,parser
602
603 [handlers]
604 keys=hand1, hand2
605
606 [formatters]
607 keys=form1, form2
608
609 [logger_root]
610 level=WARNING
611 handlers=
612
613 [logger_parser]
614 level=DEBUG
615 handlers=hand1
616 propagate=1
617 qualname=compiler.parser
618
619 [handler_hand1]
620 class=StreamHandler
621 level=NOTSET
622 formatter=form1
623 args=(sys.stdout,)
624
625 [handler_hand2]
Vinay Sajip844f7412008-09-09 13:42:08 +0000626 class=StreamHandler
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000627 level=NOTSET
628 formatter=form1
Vinay Sajip844f7412008-09-09 13:42:08 +0000629 args=(sys.stderr,)
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000630
631 [formatter_form1]
632 format=%(levelname)s ++ %(message)s
633 datefmt=
634
635 [formatter_form2]
636 format=%(message)s
637 datefmt=
638 """
639
Brett Cannon56c4deb2008-03-03 00:38:58 +0000640 def apply_config(self, conf):
Vinay Sajip28c382f2010-02-04 18:48:53 +0000641 file = cStringIO.StringIO(textwrap.dedent(conf))
642 logging.config.fileConfig(file)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000643
644 def test_config0_ok(self):
645 # A simple config file which overrides the default settings.
646 with captured_stdout() as output:
647 self.apply_config(self.config0)
648 logger = logging.getLogger()
649 # Won't output anything
650 logger.info(self.next_message())
651 # Outputs a message
652 logger.error(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000653 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000654 ('ERROR', '2'),
655 ], stream=output)
656 # Original logger output is empty.
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000657 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000658
Vinay Sajip70fdc952008-07-18 09:00:00 +0000659 def test_config1_ok(self, config=config1):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000660 # A config file defining a sub-parser as well.
661 with captured_stdout() as output:
Vinay Sajip70fdc952008-07-18 09:00:00 +0000662 self.apply_config(config)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000663 logger = logging.getLogger("compiler.parser")
664 # Both will output a message
665 logger.info(self.next_message())
666 logger.error(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000667 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000668 ('INFO', '1'),
669 ('ERROR', '2'),
670 ], stream=output)
671 # Original logger output is empty.
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000672 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000673
674 def test_config2_failure(self):
675 # A simple config file which overrides the default settings.
676 self.assertRaises(StandardError, self.apply_config, self.config2)
677
678 def test_config3_failure(self):
679 # A simple config file which overrides the default settings.
680 self.assertRaises(StandardError, self.apply_config, self.config3)
681
682 def test_config4_ok(self):
683 # A config file specifying a custom formatter class.
684 with captured_stdout() as output:
685 self.apply_config(self.config4)
686 logger = logging.getLogger()
687 try:
688 raise RuntimeError()
689 except RuntimeError:
690 logging.exception("just testing")
691 sys.stdout.seek(0)
692 self.assertEquals(output.getvalue(),
693 "ERROR:root:just testing\nGot a [RuntimeError]\n")
694 # Original logger output is empty
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000695 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000696
Vinay Sajip70fdc952008-07-18 09:00:00 +0000697 def test_config5_ok(self):
698 self.test_config1_ok(config=self.config5)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000699
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000700 def test_config6_ok(self):
701 self.test_config1_ok(config=self.config6)
702
Brett Cannon56c4deb2008-03-03 00:38:58 +0000703class LogRecordStreamHandler(StreamRequestHandler):
704
705 """Handler for a streaming logging request. It saves the log message in the
706 TCP server's 'log_output' attribute."""
707
708 TCP_LOG_END = "!!!END!!!"
709
710 def handle(self):
711 """Handle multiple requests - each expected to be of 4-byte length,
712 followed by the LogRecord in pickle format. Logs the record
713 according to whatever policy is configured locally."""
714 while True:
715 chunk = self.connection.recv(4)
716 if len(chunk) < 4:
717 break
718 slen = struct.unpack(">L", chunk)[0]
719 chunk = self.connection.recv(slen)
720 while len(chunk) < slen:
721 chunk = chunk + self.connection.recv(slen - len(chunk))
722 obj = self.unpickle(chunk)
723 record = logging.makeLogRecord(obj)
724 self.handle_log_record(record)
725
726 def unpickle(self, data):
727 return cPickle.loads(data)
728
729 def handle_log_record(self, record):
730 # If the end-of-messages sentinel is seen, tell the server to
731 # terminate.
732 if self.TCP_LOG_END in record.msg:
733 self.server.abort = 1
734 return
735 self.server.log_output += record.msg + "\n"
736
Guido van Rossum376e6362003-04-25 14:22:00 +0000737
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000738class LogRecordSocketReceiver(ThreadingTCPServer):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000739
740 """A simple-minded TCP socket-based logging receiver suitable for test
741 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000742
743 allow_reuse_address = 1
Brett Cannon56c4deb2008-03-03 00:38:58 +0000744 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000745
746 def __init__(self, host='localhost',
747 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
748 handler=LogRecordStreamHandler):
749 ThreadingTCPServer.__init__(self, (host, port), handler)
Georg Brandl57826cf2008-02-23 15:06:25 +0000750 self.abort = False
Brett Cannon56c4deb2008-03-03 00:38:58 +0000751 self.timeout = 0.1
752 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000753
754 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000755 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000756 rd, wr, ex = select.select([self.socket.fileno()], [], [],
757 self.timeout)
758 if rd:
759 self.handle_request()
Brett Cannon56c4deb2008-03-03 00:38:58 +0000760 # Notify the main thread that we're about to exit
761 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000762 # close the listen socket
763 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000764
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000765
Brett Cannon56c4deb2008-03-03 00:38:58 +0000766class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000767
Brett Cannon56c4deb2008-03-03 00:38:58 +0000768 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000769
Brett Cannon56c4deb2008-03-03 00:38:58 +0000770 def setUp(self):
771 """Set up a TCP server to receive log messages, and a SocketHandler
772 pointing to that server's address and port."""
773 BaseTest.setUp(self)
774 self.tcpserver = LogRecordSocketReceiver(port=0)
775 self.port = self.tcpserver.socket.getsockname()[1]
776 self.threads = [
777 threading.Thread(target=self.tcpserver.serve_until_stopped)]
778 for thread in self.threads:
779 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000780
Brett Cannon56c4deb2008-03-03 00:38:58 +0000781 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
782 self.sock_hdlr.setFormatter(self.root_formatter)
783 self.root_logger.removeHandler(self.root_logger.handlers[0])
784 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000785
Brett Cannon56c4deb2008-03-03 00:38:58 +0000786 def tearDown(self):
787 """Shutdown the TCP server."""
788 try:
789 self.tcpserver.abort = True
790 del self.tcpserver
791 self.root_logger.removeHandler(self.sock_hdlr)
792 self.sock_hdlr.close()
793 for thread in self.threads:
794 thread.join(2.0)
795 finally:
796 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000797
Brett Cannon56c4deb2008-03-03 00:38:58 +0000798 def get_output(self):
799 """Get the log output as received by the TCP server."""
800 # Signal the TCP receiver and wait for it to terminate.
801 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
802 self.tcpserver.finished.wait(2.0)
803 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000804
Brett Cannon56c4deb2008-03-03 00:38:58 +0000805 def test_output(self):
806 # The log message sent to the SocketHandler is properly received.
807 logger = logging.getLogger("tcp")
808 logger.error("spam")
809 logger.debug("eggs")
810 self.assertEquals(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000811
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000812
Brett Cannon56c4deb2008-03-03 00:38:58 +0000813class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000814
Brett Cannon56c4deb2008-03-03 00:38:58 +0000815 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000816
Brett Cannon56c4deb2008-03-03 00:38:58 +0000817 def setUp(self):
818 """Create a dict to remember potentially destroyed objects."""
819 BaseTest.setUp(self)
820 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000821
Brett Cannon56c4deb2008-03-03 00:38:58 +0000822 def _watch_for_survival(self, *args):
823 """Watch the given objects for survival, by creating weakrefs to
824 them."""
825 for obj in args:
826 key = id(obj), repr(obj)
827 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000828
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000829 def _assertTruesurvival(self):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000830 """Assert that all objects watched for survival have survived."""
831 # Trigger cycle breaking.
832 gc.collect()
833 dead = []
834 for (id_, repr_), ref in self._survivors.items():
835 if ref() is None:
836 dead.append(repr_)
837 if dead:
838 self.fail("%d objects should have survived "
839 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000840
Brett Cannon56c4deb2008-03-03 00:38:58 +0000841 def test_persistent_loggers(self):
842 # Logger objects are persistent and retain their configuration, even
843 # if visible references are destroyed.
844 self.root_logger.setLevel(logging.INFO)
845 foo = logging.getLogger("foo")
846 self._watch_for_survival(foo)
847 foo.setLevel(logging.DEBUG)
848 self.root_logger.debug(self.next_message())
849 foo.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000850 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000851 ('foo', 'DEBUG', '2'),
852 ])
853 del foo
854 # foo has survived.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000855 self._assertTruesurvival()
Brett Cannon56c4deb2008-03-03 00:38:58 +0000856 # foo has retained its settings.
857 bar = logging.getLogger("foo")
858 bar.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000859 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000860 ('foo', 'DEBUG', '2'),
861 ('foo', 'DEBUG', '3'),
862 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000863
Vinay Sajipb20af942009-02-08 19:06:08 +0000864
Vinay Sajip65d66e12008-09-04 07:31:21 +0000865class EncodingTest(BaseTest):
866 def test_encoding_plain_file(self):
867 # In Python 2.x, a plain file object is treated as having no encoding.
868 log = logging.getLogger("test")
869 fn = tempfile.mktemp(".log")
870 # the non-ascii data we write to the log.
871 data = "foo\x80"
872 try:
873 handler = logging.FileHandler(fn)
874 log.addHandler(handler)
875 try:
876 # write non-ascii data to the log.
877 log.warning(data)
878 finally:
879 log.removeHandler(handler)
880 handler.close()
881 # check we wrote exactly those bytes, ignoring trailing \n etc
882 f = open(fn)
883 try:
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000884 self.assertEqual(f.read().rstrip(), data)
Vinay Sajip65d66e12008-09-04 07:31:21 +0000885 finally:
886 f.close()
887 finally:
888 if os.path.isfile(fn):
889 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000890
Vinay Sajipb20af942009-02-08 19:06:08 +0000891 def test_encoding_cyrillic_unicode(self):
892 log = logging.getLogger("test")
893 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
894 message = u'\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
895 #Ensure it's written in a Cyrillic encoding
896 writer_class = codecs.getwriter('cp1251')
Vinay Sajip74f04502009-04-16 19:07:37 +0000897 writer_class.encoding = 'cp1251'
Vinay Sajipb20af942009-02-08 19:06:08 +0000898 stream = cStringIO.StringIO()
899 writer = writer_class(stream, 'strict')
900 handler = logging.StreamHandler(writer)
901 log.addHandler(handler)
902 try:
903 log.warning(message)
904 finally:
905 log.removeHandler(handler)
906 handler.close()
907 # check we wrote exactly those bytes, ignoring trailing \n etc
908 s = stream.getvalue()
909 #Compare against what the data should be when encoded in CP-1251
910 self.assertEqual(s, '\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
911
912
Vinay Sajip213faca2008-12-03 23:22:58 +0000913class WarningsTest(BaseTest):
Brett Cannon2da4d622009-04-01 19:57:10 +0000914
Vinay Sajip213faca2008-12-03 23:22:58 +0000915 def test_warnings(self):
Brett Cannon2da4d622009-04-01 19:57:10 +0000916 with warnings.catch_warnings():
Vinay Sajip1e566ce2009-04-05 11:06:24 +0000917 logging.captureWarnings(True)
Brett Cannon2da4d622009-04-01 19:57:10 +0000918 try:
Vinay Sajip14bf0a02009-04-07 17:18:24 +0000919 warnings.filterwarnings("always", category=UserWarning)
Brett Cannon2da4d622009-04-01 19:57:10 +0000920 file = cStringIO.StringIO()
921 h = logging.StreamHandler(file)
922 logger = logging.getLogger("py.warnings")
923 logger.addHandler(h)
924 warnings.warn("I'm warning you...")
925 logger.removeHandler(h)
926 s = file.getvalue()
927 h.close()
928 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Vinay Sajip213faca2008-12-03 23:22:58 +0000929
Brett Cannon2da4d622009-04-01 19:57:10 +0000930 #See if an explicit file uses the original implementation
931 file = cStringIO.StringIO()
932 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
933 file, "Dummy line")
934 s = file.getvalue()
935 file.close()
936 self.assertEqual(s,
937 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
938 finally:
939 logging.captureWarnings(False)
Vinay Sajip213faca2008-12-03 23:22:58 +0000940
Vinay Sajip28c382f2010-02-04 18:48:53 +0000941
942def formatFunc(format, datefmt=None):
943 return logging.Formatter(format, datefmt)
944
945def handlerFunc():
946 return logging.StreamHandler()
947
948class CustomHandler(logging.StreamHandler):
949 pass
950
951class ConfigDictTest(BaseTest):
952
953 """Reading logging config from a dictionary."""
954
955 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
956
957 # config0 is a standard configuration.
958 config0 = {
959 'formatters': {
960 'form1' : {
961 'format' : '%(levelname)s ++ %(message)s',
962 },
963 },
964 'handlers' : {
965 'hand1' : {
966 'class' : 'logging.StreamHandler',
967 'formatter' : 'form1',
968 'level' : 'NOTSET',
969 'stream' : 'ext://sys.stdout',
970 },
971 },
972 'root' : {
973 'level' : 'WARNING',
974 'handlers' : ['hand1'],
975 },
976 }
977
978 # config1 adds a little to the standard configuration.
979 config1 = {
980 'formatters': {
981 'form1' : {
982 'format' : '%(levelname)s ++ %(message)s',
983 },
984 },
985 'handlers' : {
986 'hand1' : {
987 'class' : 'logging.StreamHandler',
988 'formatter' : 'form1',
989 'level' : 'NOTSET',
990 'stream' : 'ext://sys.stdout',
991 },
992 },
993 'loggers' : {
994 'compiler.parser' : {
995 'level' : 'DEBUG',
996 'handlers' : ['hand1'],
997 },
998 },
999 'root' : {
1000 'level' : 'WARNING',
1001 },
1002 }
1003
1004 # config2 has a subtle configuration error that should be reported
1005 config2 = {
1006 'formatters': {
1007 'form1' : {
1008 'format' : '%(levelname)s ++ %(message)s',
1009 },
1010 },
1011 'handlers' : {
1012 'hand1' : {
1013 'class' : 'logging.StreamHandler',
1014 'formatter' : 'form1',
1015 'level' : 'NOTSET',
1016 'stream' : 'ext://sys.stdbout',
1017 },
1018 },
1019 'loggers' : {
1020 'compiler.parser' : {
1021 'level' : 'DEBUG',
1022 'handlers' : ['hand1'],
1023 },
1024 },
1025 'root' : {
1026 'level' : 'WARNING',
1027 },
1028 }
1029
1030 #As config1 but with a misspelt level on a handler
1031 config2a = {
1032 'formatters': {
1033 'form1' : {
1034 'format' : '%(levelname)s ++ %(message)s',
1035 },
1036 },
1037 'handlers' : {
1038 'hand1' : {
1039 'class' : 'logging.StreamHandler',
1040 'formatter' : 'form1',
1041 'level' : 'NTOSET',
1042 'stream' : 'ext://sys.stdout',
1043 },
1044 },
1045 'loggers' : {
1046 'compiler.parser' : {
1047 'level' : 'DEBUG',
1048 'handlers' : ['hand1'],
1049 },
1050 },
1051 'root' : {
1052 'level' : 'WARNING',
1053 },
1054 }
1055
1056
1057 #As config1 but with a misspelt level on a logger
1058 config2b = {
1059 'formatters': {
1060 'form1' : {
1061 'format' : '%(levelname)s ++ %(message)s',
1062 },
1063 },
1064 'handlers' : {
1065 'hand1' : {
1066 'class' : 'logging.StreamHandler',
1067 'formatter' : 'form1',
1068 'level' : 'NOTSET',
1069 'stream' : 'ext://sys.stdout',
1070 },
1071 },
1072 'loggers' : {
1073 'compiler.parser' : {
1074 'level' : 'DEBUG',
1075 'handlers' : ['hand1'],
1076 },
1077 },
1078 'root' : {
1079 'level' : 'WRANING',
1080 },
1081 }
1082
1083 # config3 has a less subtle configuration error
1084 config3 = {
1085 'formatters': {
1086 'form1' : {
1087 'format' : '%(levelname)s ++ %(message)s',
1088 },
1089 },
1090 'handlers' : {
1091 'hand1' : {
1092 'class' : 'logging.StreamHandler',
1093 'formatter' : 'misspelled_name',
1094 'level' : 'NOTSET',
1095 'stream' : 'ext://sys.stdout',
1096 },
1097 },
1098 'loggers' : {
1099 'compiler.parser' : {
1100 'level' : 'DEBUG',
1101 'handlers' : ['hand1'],
1102 },
1103 },
1104 'root' : {
1105 'level' : 'WARNING',
1106 },
1107 }
1108
1109 # config4 specifies a custom formatter class to be loaded
1110 config4 = {
1111 'formatters': {
1112 'form1' : {
1113 '()' : __name__ + '.ExceptionFormatter',
1114 'format' : '%(levelname)s:%(name)s:%(message)s',
1115 },
1116 },
1117 'handlers' : {
1118 'hand1' : {
1119 'class' : 'logging.StreamHandler',
1120 'formatter' : 'form1',
1121 'level' : 'NOTSET',
1122 'stream' : 'ext://sys.stdout',
1123 },
1124 },
1125 'root' : {
1126 'level' : 'NOTSET',
1127 'handlers' : ['hand1'],
1128 },
1129 }
1130
1131 # As config4 but using an actual callable rather than a string
1132 config4a = {
1133 'formatters': {
1134 'form1' : {
1135 '()' : ExceptionFormatter,
1136 'format' : '%(levelname)s:%(name)s:%(message)s',
1137 },
1138 'form2' : {
1139 '()' : __name__ + '.formatFunc',
1140 'format' : '%(levelname)s:%(name)s:%(message)s',
1141 },
1142 'form3' : {
1143 '()' : formatFunc,
1144 'format' : '%(levelname)s:%(name)s:%(message)s',
1145 },
1146 },
1147 'handlers' : {
1148 'hand1' : {
1149 'class' : 'logging.StreamHandler',
1150 'formatter' : 'form1',
1151 'level' : 'NOTSET',
1152 'stream' : 'ext://sys.stdout',
1153 },
1154 'hand2' : {
1155 '()' : handlerFunc,
1156 },
1157 },
1158 'root' : {
1159 'level' : 'NOTSET',
1160 'handlers' : ['hand1'],
1161 },
1162 }
1163
1164 # config5 specifies a custom handler class to be loaded
1165 config5 = {
1166 'formatters': {
1167 'form1' : {
1168 'format' : '%(levelname)s ++ %(message)s',
1169 },
1170 },
1171 'handlers' : {
1172 'hand1' : {
1173 'class' : __name__ + '.CustomHandler',
1174 'formatter' : 'form1',
1175 'level' : 'NOTSET',
1176 'stream' : 'ext://sys.stdout',
1177 },
1178 },
1179 'loggers' : {
1180 'compiler.parser' : {
1181 'level' : 'DEBUG',
1182 'handlers' : ['hand1'],
1183 },
1184 },
1185 'root' : {
1186 'level' : 'WARNING',
1187 },
1188 }
1189
1190 # config6 specifies a custom handler class to be loaded
1191 # but has bad arguments
1192 config6 = {
1193 'formatters': {
1194 'form1' : {
1195 'format' : '%(levelname)s ++ %(message)s',
1196 },
1197 },
1198 'handlers' : {
1199 'hand1' : {
1200 'class' : __name__ + '.CustomHandler',
1201 'formatter' : 'form1',
1202 'level' : 'NOTSET',
1203 'stream' : 'ext://sys.stdout',
1204 '9' : 'invalid parameter name',
1205 },
1206 },
1207 'loggers' : {
1208 'compiler.parser' : {
1209 'level' : 'DEBUG',
1210 'handlers' : ['hand1'],
1211 },
1212 },
1213 'root' : {
1214 'level' : 'WARNING',
1215 },
1216 }
1217
1218 #config 7 does not define compiler.parser but defines compiler.lexer
1219 #so compiler.parser should be disabled after applying it
1220 config7 = {
1221 'formatters': {
1222 'form1' : {
1223 'format' : '%(levelname)s ++ %(message)s',
1224 },
1225 },
1226 'handlers' : {
1227 'hand1' : {
1228 'class' : 'logging.StreamHandler',
1229 'formatter' : 'form1',
1230 'level' : 'NOTSET',
1231 'stream' : 'ext://sys.stdout',
1232 },
1233 },
1234 'loggers' : {
1235 'compiler.lexer' : {
1236 'level' : 'DEBUG',
1237 'handlers' : ['hand1'],
1238 },
1239 },
1240 'root' : {
1241 'level' : 'WARNING',
1242 },
1243 }
1244
1245 config8 = {
1246 'disable_existing_loggers' : False,
1247 'formatters': {
1248 'form1' : {
1249 'format' : '%(levelname)s ++ %(message)s',
1250 },
1251 },
1252 'handlers' : {
1253 'hand1' : {
1254 'class' : 'logging.StreamHandler',
1255 'formatter' : 'form1',
1256 'level' : 'NOTSET',
1257 'stream' : 'ext://sys.stdout',
1258 },
1259 },
1260 'loggers' : {
1261 'compiler' : {
1262 'level' : 'DEBUG',
1263 'handlers' : ['hand1'],
1264 },
1265 'compiler.lexer' : {
1266 },
1267 },
1268 'root' : {
1269 'level' : 'WARNING',
1270 },
1271 }
1272
1273 config9 = {
1274 'formatters': {
1275 'form1' : {
1276 'format' : '%(levelname)s ++ %(message)s',
1277 },
1278 },
1279 'handlers' : {
1280 'hand1' : {
1281 'class' : 'logging.StreamHandler',
1282 'formatter' : 'form1',
1283 'level' : 'WARNING',
1284 'stream' : 'ext://sys.stdout',
1285 },
1286 },
1287 'loggers' : {
1288 'compiler.parser' : {
1289 'level' : 'WARNING',
1290 'handlers' : ['hand1'],
1291 },
1292 },
1293 'root' : {
1294 'level' : 'NOTSET',
1295 },
1296 }
1297
1298 config9a = {
1299 'incremental' : True,
1300 'handlers' : {
1301 'hand1' : {
1302 'level' : 'WARNING',
1303 },
1304 },
1305 'loggers' : {
1306 'compiler.parser' : {
1307 'level' : 'INFO',
1308 },
1309 },
1310 }
1311
1312 config9b = {
1313 'incremental' : True,
1314 'handlers' : {
1315 'hand1' : {
1316 'level' : 'INFO',
1317 },
1318 },
1319 'loggers' : {
1320 'compiler.parser' : {
1321 'level' : 'INFO',
1322 },
1323 },
1324 }
1325
1326 #As config1 but with a filter added
1327 config10 = {
1328 'formatters': {
1329 'form1' : {
1330 'format' : '%(levelname)s ++ %(message)s',
1331 },
1332 },
1333 'filters' : {
1334 'filt1' : {
1335 'name' : 'compiler.parser',
1336 },
1337 },
1338 'handlers' : {
1339 'hand1' : {
1340 'class' : 'logging.StreamHandler',
1341 'formatter' : 'form1',
1342 'level' : 'NOTSET',
1343 'stream' : 'ext://sys.stdout',
1344 'filters' : ['filt1'],
1345 },
1346 },
1347 'loggers' : {
1348 'compiler.parser' : {
1349 'level' : 'DEBUG',
1350 'filters' : ['filt1'],
1351 },
1352 },
1353 'root' : {
1354 'level' : 'WARNING',
1355 'handlers' : ['hand1'],
1356 },
1357 }
1358
Vinay Sajipf7610b22010-02-04 21:40:56 +00001359 #As config1 but using cfg:// references
1360 config11 = {
1361 'true_formatters': {
1362 'form1' : {
1363 'format' : '%(levelname)s ++ %(message)s',
1364 },
1365 },
1366 'handler_configs': {
1367 'hand1' : {
1368 'class' : 'logging.StreamHandler',
1369 'formatter' : 'form1',
1370 'level' : 'NOTSET',
1371 'stream' : 'ext://sys.stdout',
1372 },
1373 },
1374 'formatters' : 'cfg://true_formatters',
1375 'handlers' : {
1376 'hand1' : 'cfg://handler_configs[hand1]',
1377 },
1378 'loggers' : {
1379 'compiler.parser' : {
1380 'level' : 'DEBUG',
1381 'handlers' : ['hand1'],
1382 },
1383 },
1384 'root' : {
1385 'level' : 'WARNING',
1386 },
1387 }
1388
Vinay Sajip28c382f2010-02-04 18:48:53 +00001389 def apply_config(self, conf):
1390 logging.config.dictConfig(conf)
1391
1392 def test_config0_ok(self):
1393 # A simple config which overrides the default settings.
1394 with captured_stdout() as output:
1395 self.apply_config(self.config0)
1396 logger = logging.getLogger()
1397 # Won't output anything
1398 logger.info(self.next_message())
1399 # Outputs a message
1400 logger.error(self.next_message())
1401 self.assert_log_lines([
1402 ('ERROR', '2'),
1403 ], stream=output)
1404 # Original logger output is empty.
1405 self.assert_log_lines([])
1406
1407 def test_config1_ok(self, config=config1):
1408 # A config defining a sub-parser as well.
1409 with captured_stdout() as output:
1410 self.apply_config(config)
1411 logger = logging.getLogger("compiler.parser")
1412 # Both will output a message
1413 logger.info(self.next_message())
1414 logger.error(self.next_message())
1415 self.assert_log_lines([
1416 ('INFO', '1'),
1417 ('ERROR', '2'),
1418 ], stream=output)
1419 # Original logger output is empty.
1420 self.assert_log_lines([])
1421
1422 def test_config2_failure(self):
1423 # A simple config which overrides the default settings.
1424 self.assertRaises(StandardError, self.apply_config, self.config2)
1425
1426 def test_config2a_failure(self):
1427 # A simple config which overrides the default settings.
1428 self.assertRaises(StandardError, self.apply_config, self.config2a)
1429
1430 def test_config2b_failure(self):
1431 # A simple config which overrides the default settings.
1432 self.assertRaises(StandardError, self.apply_config, self.config2b)
1433
1434 def test_config3_failure(self):
1435 # A simple config which overrides the default settings.
1436 self.assertRaises(StandardError, self.apply_config, self.config3)
1437
1438 def test_config4_ok(self):
1439 # A config specifying a custom formatter class.
1440 with captured_stdout() as output:
1441 self.apply_config(self.config4)
1442 #logger = logging.getLogger()
1443 try:
1444 raise RuntimeError()
1445 except RuntimeError:
1446 logging.exception("just testing")
1447 sys.stdout.seek(0)
1448 self.assertEquals(output.getvalue(),
1449 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1450 # Original logger output is empty
1451 self.assert_log_lines([])
1452
1453 def test_config4a_ok(self):
1454 # A config specifying a custom formatter class.
1455 with captured_stdout() as output:
1456 self.apply_config(self.config4a)
1457 #logger = logging.getLogger()
1458 try:
1459 raise RuntimeError()
1460 except RuntimeError:
1461 logging.exception("just testing")
1462 sys.stdout.seek(0)
1463 self.assertEquals(output.getvalue(),
1464 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1465 # Original logger output is empty
1466 self.assert_log_lines([])
1467
1468 def test_config5_ok(self):
1469 self.test_config1_ok(config=self.config5)
1470
1471 def test_config6_failure(self):
1472 self.assertRaises(StandardError, self.apply_config, self.config6)
1473
1474 def test_config7_ok(self):
1475 with captured_stdout() as output:
1476 self.apply_config(self.config1)
1477 logger = logging.getLogger("compiler.parser")
1478 # Both will output a message
1479 logger.info(self.next_message())
1480 logger.error(self.next_message())
1481 self.assert_log_lines([
1482 ('INFO', '1'),
1483 ('ERROR', '2'),
1484 ], stream=output)
1485 # Original logger output is empty.
1486 self.assert_log_lines([])
1487 with captured_stdout() as output:
1488 self.apply_config(self.config7)
1489 logger = logging.getLogger("compiler.parser")
1490 self.assertTrue(logger.disabled)
1491 logger = logging.getLogger("compiler.lexer")
1492 # Both will output a message
1493 logger.info(self.next_message())
1494 logger.error(self.next_message())
1495 self.assert_log_lines([
1496 ('INFO', '3'),
1497 ('ERROR', '4'),
1498 ], stream=output)
1499 # Original logger output is empty.
1500 self.assert_log_lines([])
1501
1502 #Same as test_config_7_ok but don't disable old loggers.
1503 def test_config_8_ok(self):
1504 with captured_stdout() as output:
1505 self.apply_config(self.config1)
1506 logger = logging.getLogger("compiler.parser")
1507 # Both will output a message
1508 logger.info(self.next_message())
1509 logger.error(self.next_message())
1510 self.assert_log_lines([
1511 ('INFO', '1'),
1512 ('ERROR', '2'),
1513 ], stream=output)
1514 # Original logger output is empty.
1515 self.assert_log_lines([])
1516 with captured_stdout() as output:
1517 self.apply_config(self.config8)
1518 logger = logging.getLogger("compiler.parser")
1519 self.assertFalse(logger.disabled)
1520 # Both will output a message
1521 logger.info(self.next_message())
1522 logger.error(self.next_message())
1523 logger = logging.getLogger("compiler.lexer")
1524 # Both will output a message
1525 logger.info(self.next_message())
1526 logger.error(self.next_message())
1527 self.assert_log_lines([
1528 ('INFO', '3'),
1529 ('ERROR', '4'),
1530 ('INFO', '5'),
1531 ('ERROR', '6'),
1532 ], stream=output)
1533 # Original logger output is empty.
1534 self.assert_log_lines([])
1535
1536 def test_config_9_ok(self):
1537 with captured_stdout() as output:
1538 self.apply_config(self.config9)
1539 logger = logging.getLogger("compiler.parser")
1540 #Nothing will be output since both handler and logger are set to WARNING
1541 logger.info(self.next_message())
1542 self.assert_log_lines([], stream=output)
1543 self.apply_config(self.config9a)
1544 #Nothing will be output since both handler is still set to WARNING
1545 logger.info(self.next_message())
1546 self.assert_log_lines([], stream=output)
1547 self.apply_config(self.config9b)
1548 #Message should now be output
1549 logger.info(self.next_message())
1550 self.assert_log_lines([
1551 ('INFO', '3'),
1552 ], stream=output)
1553
1554 def test_config_10_ok(self):
1555 with captured_stdout() as output:
1556 self.apply_config(self.config10)
1557 logger = logging.getLogger("compiler.parser")
1558 logger.warning(self.next_message())
1559 logger = logging.getLogger('compiler')
1560 #Not output, because filtered
1561 logger.warning(self.next_message())
1562 logger = logging.getLogger('compiler.lexer')
1563 #Not output, because filtered
1564 logger.warning(self.next_message())
1565 logger = logging.getLogger("compiler.parser.codegen")
1566 #Output, as not filtered
1567 logger.error(self.next_message())
1568 self.assert_log_lines([
1569 ('WARNING', '1'),
1570 ('ERROR', '4'),
1571 ], stream=output)
1572
Vinay Sajipf7610b22010-02-04 21:40:56 +00001573 def test_config11_ok(self):
1574 self.test_config1_ok(self.config11)
1575
Vinay Sajip28c382f2010-02-04 18:48:53 +00001576 def setup_via_listener(self, text):
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001577 port = find_unused_port()
1578 t = logging.config.listen(port)
Vinay Sajip28c382f2010-02-04 18:48:53 +00001579 t.start()
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001580 try:
1581 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Vinay Sajip23739262010-02-05 23:43:11 +00001582 sock.settimeout(2.0)
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001583 sock.connect(('localhost', port))
Vinay Sajip28c382f2010-02-04 18:48:53 +00001584
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001585 slen = struct.pack('>L', len(text))
1586 s = slen + text
1587 sentsofar = 0
1588 left = len(s)
1589 while left > 0:
1590 sent = sock.send(s[sentsofar:])
1591 sentsofar += sent
1592 left -= sent
1593 sock.close()
1594 finally:
1595 logging.config.stopListening()
Vinay Sajip23739262010-02-05 23:43:11 +00001596 t.join(2.0)
Vinay Sajip28c382f2010-02-04 18:48:53 +00001597
Vinay Sajip23739262010-02-05 23:43:11 +00001598 @unittest.skip("See issue #7857")
1599 def test_listen_config_10_ok(self):
Vinay Sajip28c382f2010-02-04 18:48:53 +00001600 with captured_stdout() as output:
1601 self.setup_via_listener(json.dumps(self.config10))
1602 logger = logging.getLogger("compiler.parser")
1603 logger.warning(self.next_message())
1604 logger = logging.getLogger('compiler')
1605 #Not output, because filtered
1606 logger.warning(self.next_message())
1607 logger = logging.getLogger('compiler.lexer')
1608 #Not output, because filtered
1609 logger.warning(self.next_message())
1610 logger = logging.getLogger("compiler.parser.codegen")
1611 #Output, as not filtered
1612 logger.error(self.next_message())
1613 self.assert_log_lines([
1614 ('WARNING', '1'),
1615 ('ERROR', '4'),
1616 ], stream=output)
1617
Vinay Sajip23739262010-02-05 23:43:11 +00001618 @unittest.skip("See issue #7857")
1619 def test_listen_config_1_ok(self):
Vinay Sajip28c382f2010-02-04 18:48:53 +00001620 with captured_stdout() as output:
1621 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
1622 logger = logging.getLogger("compiler.parser")
1623 # Both will output a message
1624 logger.info(self.next_message())
1625 logger.error(self.next_message())
1626 self.assert_log_lines([
1627 ('INFO', '1'),
1628 ('ERROR', '2'),
1629 ], stream=output)
1630 # Original logger output is empty.
1631 self.assert_log_lines([])
1632
1633
Brett Cannon56c4deb2008-03-03 00:38:58 +00001634# Set the locale to the platform-dependent default. I have no idea
1635# why the test does this, but in any case we save the current locale
1636# first and restore it at the end.
1637@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00001638def test_main():
Brett Cannon56c4deb2008-03-03 00:38:58 +00001639 run_unittest(BuiltinLevelsTest, BasicFilterTest,
1640 CustomLevelsAndFiltersTest, MemoryHandlerTest,
Vinay Sajip65d66e12008-09-04 07:31:21 +00001641 ConfigFileTest, SocketHandlerTest, MemoryTest,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001642 EncodingTest, WarningsTest, ConfigDictTest)
Jeremy Hylton096d9862003-07-18 03:19:20 +00001643
Brett Cannon56c4deb2008-03-03 00:38:58 +00001644if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001645 test_main()