blob: 88332b5a85491603f3dd3736d673ec6d64ecce75 [file] [log] [blame]
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001#!/usr/bin/env python
Brett Cannon56c4deb2008-03-03 00:38:58 +00002#
Vinay Sajip5820cc02012-04-24 23:17:29 +01003# Copyright 2001-2012 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
Vinay Sajip5820cc02012-04-24 23:17:29 +010021Copyright (C) 2001-2012 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
Vinay Sajip885f6fd2012-04-24 23:15:10 +010034import random
Brett Cannon56c4deb2008-03-03 00:38:58 +000035import 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 struct
40import sys
41import tempfile
Vinay Sajip27a13702010-05-03 15:11:53 +000042from test.test_support import captured_stdout, run_with_locale, run_unittest
Brett Cannon56c4deb2008-03-03 00:38:58 +000043import textwrap
Vinay Sajip885f6fd2012-04-24 23:15:10 +010044import time
Brett Cannon56c4deb2008-03-03 00:38:58 +000045import unittest
Vinay Sajip213faca2008-12-03 23:22:58 +000046import warnings
Brett Cannon56c4deb2008-03-03 00:38:58 +000047import weakref
Victor Stinner6a102812010-04-27 23:55:59 +000048try:
49 import threading
50except ImportError:
51 threading = None
Brett Cannonf9db8a32008-02-17 01:59:18 +000052
Brett Cannon56c4deb2008-03-03 00:38:58 +000053class BaseTest(unittest.TestCase):
54
55 """Base class for logging tests."""
56
57 log_format = "%(name)s -> %(levelname)s: %(message)s"
58 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
59 message_num = 0
60
61 def setUp(self):
62 """Setup the default logging stream to an internal StringIO instance,
63 so that we can examine log output as we want."""
64 logger_dict = logging.getLogger().manager.loggerDict
Brett Cannonf9db8a32008-02-17 01:59:18 +000065 logging._acquireLock()
66 try:
Brett Cannon56c4deb2008-03-03 00:38:58 +000067 self.saved_handlers = logging._handlers.copy()
68 self.saved_handler_list = logging._handlerList[:]
69 self.saved_loggers = logger_dict.copy()
70 self.saved_level_names = logging._levelNames.copy()
Brett Cannonf9db8a32008-02-17 01:59:18 +000071 finally:
72 logging._releaseLock()
73
Vinay Sajip31e928e2010-03-22 13:02:28 +000074 # Set two unused loggers: one non-ASCII and one Unicode.
75 # This is to test correct operation when sorting existing
76 # loggers in the configuration code. See issue 8201.
77 logging.getLogger("\xab\xd7\xbb")
78 logging.getLogger(u"\u013f\u00d6\u0047")
79
Brett Cannon56c4deb2008-03-03 00:38:58 +000080 self.root_logger = logging.getLogger("")
81 self.original_logging_level = self.root_logger.getEffectiveLevel()
82
83 self.stream = cStringIO.StringIO()
84 self.root_logger.setLevel(logging.DEBUG)
85 self.root_hdlr = logging.StreamHandler(self.stream)
86 self.root_formatter = logging.Formatter(self.log_format)
87 self.root_hdlr.setFormatter(self.root_formatter)
88 self.root_logger.addHandler(self.root_hdlr)
89
90 def tearDown(self):
91 """Remove our logging stream, and restore the original logging
92 level."""
93 self.stream.close()
94 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajip28c382f2010-02-04 18:48:53 +000095 while self.root_logger.handlers:
96 h = self.root_logger.handlers[0]
97 self.root_logger.removeHandler(h)
98 h.close()
Brett Cannon56c4deb2008-03-03 00:38:58 +000099 self.root_logger.setLevel(self.original_logging_level)
100 logging._acquireLock()
101 try:
102 logging._levelNames.clear()
103 logging._levelNames.update(self.saved_level_names)
104 logging._handlers.clear()
105 logging._handlers.update(self.saved_handlers)
106 logging._handlerList[:] = self.saved_handler_list
107 loggerDict = logging.getLogger().manager.loggerDict
108 loggerDict.clear()
109 loggerDict.update(self.saved_loggers)
110 finally:
111 logging._releaseLock()
112
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000113 def assert_log_lines(self, expected_values, stream=None):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000114 """Match the collected log lines against the regular expression
115 self.expected_log_pat, and compare the extracted group values to
116 the expected_values list of tuples."""
117 stream = stream or self.stream
118 pat = re.compile(self.expected_log_pat)
119 try:
120 stream.reset()
121 actual_lines = stream.readlines()
122 except AttributeError:
123 # StringIO.StringIO lacks a reset() method.
124 actual_lines = stream.getvalue().splitlines()
Ezio Melotti2623a372010-11-21 13:34:58 +0000125 self.assertEqual(len(actual_lines), len(expected_values))
Brett Cannon56c4deb2008-03-03 00:38:58 +0000126 for actual, expected in zip(actual_lines, expected_values):
127 match = pat.search(actual)
128 if not match:
129 self.fail("Log line does not match expected pattern:\n" +
130 actual)
Ezio Melotti2623a372010-11-21 13:34:58 +0000131 self.assertEqual(tuple(match.groups()), expected)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000132 s = stream.read()
133 if s:
134 self.fail("Remaining output at end of log stream:\n" + s)
135
136 def next_message(self):
137 """Generate a message consisting solely of an auto-incrementing
138 integer."""
139 self.message_num += 1
140 return "%d" % self.message_num
141
142
143class BuiltinLevelsTest(BaseTest):
144 """Test builtin levels and their inheritance."""
145
146 def test_flat(self):
147 #Logging levels in a flat logger namespace.
148 m = self.next_message
149
150 ERR = logging.getLogger("ERR")
151 ERR.setLevel(logging.ERROR)
152 INF = logging.getLogger("INF")
153 INF.setLevel(logging.INFO)
154 DEB = logging.getLogger("DEB")
155 DEB.setLevel(logging.DEBUG)
156
157 # These should log.
158 ERR.log(logging.CRITICAL, m())
159 ERR.error(m())
160
161 INF.log(logging.CRITICAL, m())
162 INF.error(m())
163 INF.warn(m())
164 INF.info(m())
165
166 DEB.log(logging.CRITICAL, m())
167 DEB.error(m())
168 DEB.warn (m())
169 DEB.info (m())
170 DEB.debug(m())
171
172 # These should not log.
173 ERR.warn(m())
174 ERR.info(m())
175 ERR.debug(m())
176
177 INF.debug(m())
178
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000179 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000180 ('ERR', 'CRITICAL', '1'),
181 ('ERR', 'ERROR', '2'),
182 ('INF', 'CRITICAL', '3'),
183 ('INF', 'ERROR', '4'),
184 ('INF', 'WARNING', '5'),
185 ('INF', 'INFO', '6'),
186 ('DEB', 'CRITICAL', '7'),
187 ('DEB', 'ERROR', '8'),
188 ('DEB', 'WARNING', '9'),
189 ('DEB', 'INFO', '10'),
190 ('DEB', 'DEBUG', '11'),
191 ])
192
193 def test_nested_explicit(self):
194 # Logging levels in a nested namespace, all explicitly set.
195 m = self.next_message
196
197 INF = logging.getLogger("INF")
198 INF.setLevel(logging.INFO)
199 INF_ERR = logging.getLogger("INF.ERR")
200 INF_ERR.setLevel(logging.ERROR)
201
202 # These should log.
203 INF_ERR.log(logging.CRITICAL, m())
204 INF_ERR.error(m())
205
206 # These should not log.
207 INF_ERR.warn(m())
208 INF_ERR.info(m())
209 INF_ERR.debug(m())
210
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000211 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000212 ('INF.ERR', 'CRITICAL', '1'),
213 ('INF.ERR', 'ERROR', '2'),
214 ])
215
216 def test_nested_inherited(self):
217 #Logging levels in a nested namespace, inherited from parent loggers.
218 m = self.next_message
219
220 INF = logging.getLogger("INF")
221 INF.setLevel(logging.INFO)
222 INF_ERR = logging.getLogger("INF.ERR")
223 INF_ERR.setLevel(logging.ERROR)
224 INF_UNDEF = logging.getLogger("INF.UNDEF")
225 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
226 UNDEF = logging.getLogger("UNDEF")
227
228 # These should log.
229 INF_UNDEF.log(logging.CRITICAL, m())
230 INF_UNDEF.error(m())
231 INF_UNDEF.warn(m())
232 INF_UNDEF.info(m())
233 INF_ERR_UNDEF.log(logging.CRITICAL, m())
234 INF_ERR_UNDEF.error(m())
235
236 # These should not log.
237 INF_UNDEF.debug(m())
238 INF_ERR_UNDEF.warn(m())
239 INF_ERR_UNDEF.info(m())
240 INF_ERR_UNDEF.debug(m())
241
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000242 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000243 ('INF.UNDEF', 'CRITICAL', '1'),
244 ('INF.UNDEF', 'ERROR', '2'),
245 ('INF.UNDEF', 'WARNING', '3'),
246 ('INF.UNDEF', 'INFO', '4'),
247 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
248 ('INF.ERR.UNDEF', 'ERROR', '6'),
249 ])
250
251 def test_nested_with_virtual_parent(self):
252 # Logging levels when some parent does not exist yet.
253 m = self.next_message
254
255 INF = logging.getLogger("INF")
256 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
257 CHILD = logging.getLogger("INF.BADPARENT")
258 INF.setLevel(logging.INFO)
259
260 # These should log.
261 GRANDCHILD.log(logging.FATAL, m())
262 GRANDCHILD.info(m())
263 CHILD.log(logging.FATAL, m())
264 CHILD.info(m())
265
266 # These should not log.
267 GRANDCHILD.debug(m())
268 CHILD.debug(m())
269
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000270 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000271 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
272 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
273 ('INF.BADPARENT', 'CRITICAL', '3'),
274 ('INF.BADPARENT', 'INFO', '4'),
275 ])
276
Vinay Sajip74ab3442011-11-07 08:49:16 +0000277 def test_invalid_name(self):
Vinay Sajip53703d92011-11-07 10:13:18 +0000278 self.assertRaises(TypeError, logging.getLogger, any)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000279
280class BasicFilterTest(BaseTest):
281
282 """Test the bundled Filter class."""
283
284 def test_filter(self):
285 # Only messages satisfying the specified criteria pass through the
286 # filter.
287 filter_ = logging.Filter("spam.eggs")
288 handler = self.root_logger.handlers[0]
289 try:
290 handler.addFilter(filter_)
291 spam = logging.getLogger("spam")
292 spam_eggs = logging.getLogger("spam.eggs")
293 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
294 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
295
296 spam.info(self.next_message())
297 spam_eggs.info(self.next_message()) # Good.
298 spam_eggs_fish.info(self.next_message()) # Good.
299 spam_bakedbeans.info(self.next_message())
300
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000301 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000302 ('spam.eggs', 'INFO', '2'),
303 ('spam.eggs.fish', 'INFO', '3'),
304 ])
305 finally:
306 handler.removeFilter(filter_)
307
308
309#
310# First, we define our levels. There can be as many as you want - the only
311# limitations are that they should be integers, the lowest should be > 0 and
312# larger values mean less information being logged. If you need specific
313# level values which do not fit into these limitations, you can use a
314# mapping dictionary to convert between your application levels and the
315# logging system.
316#
317SILENT = 120
318TACITURN = 119
319TERSE = 118
320EFFUSIVE = 117
321SOCIABLE = 116
322VERBOSE = 115
323TALKATIVE = 114
324GARRULOUS = 113
325CHATTERBOX = 112
326BORING = 111
327
328LEVEL_RANGE = range(BORING, SILENT + 1)
329
330#
331# Next, we define names for our levels. You don't need to do this - in which
332# case the system will use "Level n" to denote the text for the level.
333#
334my_logging_levels = {
335 SILENT : 'Silent',
336 TACITURN : 'Taciturn',
337 TERSE : 'Terse',
338 EFFUSIVE : 'Effusive',
339 SOCIABLE : 'Sociable',
340 VERBOSE : 'Verbose',
341 TALKATIVE : 'Talkative',
342 GARRULOUS : 'Garrulous',
343 CHATTERBOX : 'Chatterbox',
344 BORING : 'Boring',
345}
346
347class GarrulousFilter(logging.Filter):
348
349 """A filter which blocks garrulous messages."""
350
351 def filter(self, record):
352 return record.levelno != GARRULOUS
353
354class VerySpecificFilter(logging.Filter):
355
356 """A filter which blocks sociable and taciturn messages."""
357
358 def filter(self, record):
359 return record.levelno not in [SOCIABLE, TACITURN]
360
361
362class CustomLevelsAndFiltersTest(BaseTest):
363
364 """Test various filtering possibilities with custom logging levels."""
365
366 # Skip the logger name group.
367 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
368
369 def setUp(self):
370 BaseTest.setUp(self)
371 for k, v in my_logging_levels.items():
372 logging.addLevelName(k, v)
373
374 def log_at_all_levels(self, logger):
375 for lvl in LEVEL_RANGE:
376 logger.log(lvl, self.next_message())
377
378 def test_logger_filter(self):
379 # Filter at logger level.
380 self.root_logger.setLevel(VERBOSE)
381 # Levels >= 'Verbose' are good.
382 self.log_at_all_levels(self.root_logger)
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000383 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000384 ('Verbose', '5'),
385 ('Sociable', '6'),
386 ('Effusive', '7'),
387 ('Terse', '8'),
388 ('Taciturn', '9'),
389 ('Silent', '10'),
390 ])
391
392 def test_handler_filter(self):
393 # Filter at handler level.
394 self.root_logger.handlers[0].setLevel(SOCIABLE)
395 try:
396 # Levels >= 'Sociable' are good.
397 self.log_at_all_levels(self.root_logger)
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000398 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000399 ('Sociable', '6'),
400 ('Effusive', '7'),
401 ('Terse', '8'),
402 ('Taciturn', '9'),
403 ('Silent', '10'),
404 ])
405 finally:
406 self.root_logger.handlers[0].setLevel(logging.NOTSET)
407
408 def test_specific_filters(self):
409 # Set a specific filter object on the handler, and then add another
410 # filter object on the logger itself.
411 handler = self.root_logger.handlers[0]
412 specific_filter = None
413 garr = GarrulousFilter()
414 handler.addFilter(garr)
415 try:
416 self.log_at_all_levels(self.root_logger)
417 first_lines = [
418 # Notice how 'Garrulous' is missing
419 ('Boring', '1'),
420 ('Chatterbox', '2'),
421 ('Talkative', '4'),
422 ('Verbose', '5'),
423 ('Sociable', '6'),
424 ('Effusive', '7'),
425 ('Terse', '8'),
426 ('Taciturn', '9'),
427 ('Silent', '10'),
428 ]
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000429 self.assert_log_lines(first_lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000430
431 specific_filter = VerySpecificFilter()
432 self.root_logger.addFilter(specific_filter)
433 self.log_at_all_levels(self.root_logger)
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000434 self.assert_log_lines(first_lines + [
Brett Cannon56c4deb2008-03-03 00:38:58 +0000435 # Not only 'Garrulous' is still missing, but also 'Sociable'
436 # and 'Taciturn'
437 ('Boring', '11'),
438 ('Chatterbox', '12'),
439 ('Talkative', '14'),
440 ('Verbose', '15'),
441 ('Effusive', '17'),
442 ('Terse', '18'),
443 ('Silent', '20'),
444 ])
445 finally:
446 if specific_filter:
447 self.root_logger.removeFilter(specific_filter)
448 handler.removeFilter(garr)
449
450
451class MemoryHandlerTest(BaseTest):
452
453 """Tests for the MemoryHandler."""
454
455 # Do not bother with a logger name group.
456 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
457
458 def setUp(self):
459 BaseTest.setUp(self)
460 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
461 self.root_hdlr)
462 self.mem_logger = logging.getLogger('mem')
463 self.mem_logger.propagate = 0
464 self.mem_logger.addHandler(self.mem_hdlr)
465
466 def tearDown(self):
467 self.mem_hdlr.close()
Amaury Forgeot d'Arc2aece572008-03-29 01:42:31 +0000468 BaseTest.tearDown(self)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000469
470 def test_flush(self):
471 # The memory handler flushes to its target handler based on specific
472 # criteria (message count and message level).
473 self.mem_logger.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000474 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000475 self.mem_logger.info(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000476 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000477 # This will flush because the level is >= logging.WARNING
478 self.mem_logger.warn(self.next_message())
479 lines = [
480 ('DEBUG', '1'),
481 ('INFO', '2'),
482 ('WARNING', '3'),
483 ]
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000484 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000485 for n in (4, 14):
486 for i in range(9):
487 self.mem_logger.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000488 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000489 # This will flush because it's the 10th message since the last
490 # flush.
491 self.mem_logger.debug(self.next_message())
492 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000493 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000494
495 self.mem_logger.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000496 self.assert_log_lines(lines)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000497
498
499class ExceptionFormatter(logging.Formatter):
500 """A special exception formatter."""
501 def formatException(self, ei):
502 return "Got a [%s]" % ei[0].__name__
503
504
505class ConfigFileTest(BaseTest):
506
507 """Reading logging config from a .ini-style config file."""
508
509 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
510
511 # config0 is a standard configuration.
512 config0 = """
513 [loggers]
514 keys=root
515
516 [handlers]
517 keys=hand1
518
519 [formatters]
520 keys=form1
521
522 [logger_root]
523 level=WARNING
524 handlers=hand1
525
526 [handler_hand1]
527 class=StreamHandler
528 level=NOTSET
529 formatter=form1
530 args=(sys.stdout,)
531
532 [formatter_form1]
533 format=%(levelname)s ++ %(message)s
534 datefmt=
535 """
536
537 # config1 adds a little to the standard configuration.
538 config1 = """
539 [loggers]
540 keys=root,parser
541
542 [handlers]
543 keys=hand1
544
545 [formatters]
546 keys=form1
547
548 [logger_root]
549 level=WARNING
550 handlers=
551
552 [logger_parser]
553 level=DEBUG
554 handlers=hand1
555 propagate=1
556 qualname=compiler.parser
557
558 [handler_hand1]
559 class=StreamHandler
560 level=NOTSET
561 formatter=form1
562 args=(sys.stdout,)
563
564 [formatter_form1]
565 format=%(levelname)s ++ %(message)s
566 datefmt=
567 """
568
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000569 # config1a moves the handler to the root.
570 config1a = """
571 [loggers]
572 keys=root,parser
573
574 [handlers]
575 keys=hand1
576
577 [formatters]
578 keys=form1
579
580 [logger_root]
581 level=WARNING
582 handlers=hand1
583
584 [logger_parser]
585 level=DEBUG
586 handlers=
587 propagate=1
588 qualname=compiler.parser
589
590 [handler_hand1]
591 class=StreamHandler
592 level=NOTSET
593 formatter=form1
594 args=(sys.stdout,)
595
596 [formatter_form1]
597 format=%(levelname)s ++ %(message)s
598 datefmt=
599 """
600
Brett Cannon56c4deb2008-03-03 00:38:58 +0000601 # config2 has a subtle configuration error that should be reported
602 config2 = config1.replace("sys.stdout", "sys.stbout")
603
604 # config3 has a less subtle configuration error
605 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
606
607 # config4 specifies a custom formatter class to be loaded
608 config4 = """
609 [loggers]
610 keys=root
611
612 [handlers]
613 keys=hand1
614
615 [formatters]
616 keys=form1
617
618 [logger_root]
619 level=NOTSET
620 handlers=hand1
621
622 [handler_hand1]
623 class=StreamHandler
624 level=NOTSET
625 formatter=form1
626 args=(sys.stdout,)
627
628 [formatter_form1]
629 class=""" + __name__ + """.ExceptionFormatter
630 format=%(levelname)s:%(name)s:%(message)s
631 datefmt=
632 """
633
Vinay Sajip70fdc952008-07-18 09:00:00 +0000634 # config5 specifies a custom handler class to be loaded
635 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
636
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000637 # config6 uses ', ' delimiters in the handlers and formatters sections
638 config6 = """
639 [loggers]
640 keys=root,parser
641
642 [handlers]
643 keys=hand1, hand2
644
645 [formatters]
646 keys=form1, form2
647
648 [logger_root]
649 level=WARNING
650 handlers=
651
652 [logger_parser]
653 level=DEBUG
654 handlers=hand1
655 propagate=1
656 qualname=compiler.parser
657
658 [handler_hand1]
659 class=StreamHandler
660 level=NOTSET
661 formatter=form1
662 args=(sys.stdout,)
663
664 [handler_hand2]
Vinay Sajip844f7412008-09-09 13:42:08 +0000665 class=StreamHandler
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000666 level=NOTSET
667 formatter=form1
Vinay Sajip844f7412008-09-09 13:42:08 +0000668 args=(sys.stderr,)
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000669
670 [formatter_form1]
671 format=%(levelname)s ++ %(message)s
672 datefmt=
673
674 [formatter_form2]
675 format=%(message)s
676 datefmt=
677 """
678
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000679 # config7 adds a compiler logger.
680 config7 = """
681 [loggers]
682 keys=root,parser,compiler
683
684 [handlers]
685 keys=hand1
686
687 [formatters]
688 keys=form1
689
690 [logger_root]
691 level=WARNING
692 handlers=hand1
693
694 [logger_compiler]
695 level=DEBUG
696 handlers=
697 propagate=1
698 qualname=compiler
699
700 [logger_parser]
701 level=DEBUG
702 handlers=
703 propagate=1
704 qualname=compiler.parser
705
706 [handler_hand1]
707 class=StreamHandler
708 level=NOTSET
709 formatter=form1
710 args=(sys.stdout,)
711
712 [formatter_form1]
713 format=%(levelname)s ++ %(message)s
714 datefmt=
715 """
716
Brett Cannon56c4deb2008-03-03 00:38:58 +0000717 def apply_config(self, conf):
Vinay Sajip28c382f2010-02-04 18:48:53 +0000718 file = cStringIO.StringIO(textwrap.dedent(conf))
719 logging.config.fileConfig(file)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000720
721 def test_config0_ok(self):
722 # A simple config file which overrides the default settings.
723 with captured_stdout() as output:
724 self.apply_config(self.config0)
725 logger = logging.getLogger()
726 # Won't output anything
727 logger.info(self.next_message())
728 # Outputs a message
729 logger.error(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000730 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000731 ('ERROR', '2'),
732 ], stream=output)
733 # Original logger output is empty.
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000734 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000735
Vinay Sajip70fdc952008-07-18 09:00:00 +0000736 def test_config1_ok(self, config=config1):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000737 # A config file defining a sub-parser as well.
738 with captured_stdout() as output:
Vinay Sajip70fdc952008-07-18 09:00:00 +0000739 self.apply_config(config)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000740 logger = logging.getLogger("compiler.parser")
741 # Both will output a message
742 logger.info(self.next_message())
743 logger.error(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000744 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000745 ('INFO', '1'),
746 ('ERROR', '2'),
747 ], stream=output)
748 # Original logger output is empty.
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000749 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000750
751 def test_config2_failure(self):
752 # A simple config file which overrides the default settings.
753 self.assertRaises(StandardError, self.apply_config, self.config2)
754
755 def test_config3_failure(self):
756 # A simple config file which overrides the default settings.
757 self.assertRaises(StandardError, self.apply_config, self.config3)
758
759 def test_config4_ok(self):
760 # A config file specifying a custom formatter class.
761 with captured_stdout() as output:
762 self.apply_config(self.config4)
763 logger = logging.getLogger()
764 try:
765 raise RuntimeError()
766 except RuntimeError:
767 logging.exception("just testing")
768 sys.stdout.seek(0)
Ezio Melotti2623a372010-11-21 13:34:58 +0000769 self.assertEqual(output.getvalue(),
Brett Cannon56c4deb2008-03-03 00:38:58 +0000770 "ERROR:root:just testing\nGot a [RuntimeError]\n")
771 # Original logger output is empty
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000772 self.assert_log_lines([])
Brett Cannon56c4deb2008-03-03 00:38:58 +0000773
Vinay Sajip70fdc952008-07-18 09:00:00 +0000774 def test_config5_ok(self):
775 self.test_config1_ok(config=self.config5)
Brett Cannon56c4deb2008-03-03 00:38:58 +0000776
Vinay Sajip6a2fd812008-09-03 09:20:05 +0000777 def test_config6_ok(self):
778 self.test_config1_ok(config=self.config6)
779
Vinay Sajip8dd2a402011-03-07 15:02:11 +0000780 def test_config7_ok(self):
781 with captured_stdout() as output:
782 self.apply_config(self.config1a)
783 logger = logging.getLogger("compiler.parser")
784 # See issue #11424. compiler-hyphenated sorts
785 # between compiler and compiler.xyz and this
786 # was preventing compiler.xyz from being included
787 # in the child loggers of compiler because of an
788 # overzealous loop termination condition.
789 hyphenated = logging.getLogger('compiler-hyphenated')
790 # All will output a message
791 logger.info(self.next_message())
792 logger.error(self.next_message())
793 hyphenated.critical(self.next_message())
794 self.assert_log_lines([
795 ('INFO', '1'),
796 ('ERROR', '2'),
797 ('CRITICAL', '3'),
798 ], stream=output)
799 # Original logger output is empty.
800 self.assert_log_lines([])
801 with captured_stdout() as output:
802 self.apply_config(self.config7)
803 logger = logging.getLogger("compiler.parser")
804 self.assertFalse(logger.disabled)
805 # Both will output a message
806 logger.info(self.next_message())
807 logger.error(self.next_message())
808 logger = logging.getLogger("compiler.lexer")
809 # Both will output a message
810 logger.info(self.next_message())
811 logger.error(self.next_message())
812 # Will not appear
813 hyphenated.critical(self.next_message())
814 self.assert_log_lines([
815 ('INFO', '4'),
816 ('ERROR', '5'),
817 ('INFO', '6'),
818 ('ERROR', '7'),
819 ], stream=output)
820 # Original logger output is empty.
821 self.assert_log_lines([])
Vinay Sajip923e6d22011-03-07 18:20:27 +0000822
Brett Cannon56c4deb2008-03-03 00:38:58 +0000823class LogRecordStreamHandler(StreamRequestHandler):
824
825 """Handler for a streaming logging request. It saves the log message in the
826 TCP server's 'log_output' attribute."""
827
828 TCP_LOG_END = "!!!END!!!"
829
830 def handle(self):
831 """Handle multiple requests - each expected to be of 4-byte length,
832 followed by the LogRecord in pickle format. Logs the record
833 according to whatever policy is configured locally."""
834 while True:
835 chunk = self.connection.recv(4)
836 if len(chunk) < 4:
837 break
838 slen = struct.unpack(">L", chunk)[0]
839 chunk = self.connection.recv(slen)
840 while len(chunk) < slen:
841 chunk = chunk + self.connection.recv(slen - len(chunk))
842 obj = self.unpickle(chunk)
843 record = logging.makeLogRecord(obj)
844 self.handle_log_record(record)
845
846 def unpickle(self, data):
847 return cPickle.loads(data)
848
849 def handle_log_record(self, record):
850 # If the end-of-messages sentinel is seen, tell the server to
851 # terminate.
852 if self.TCP_LOG_END in record.msg:
853 self.server.abort = 1
854 return
855 self.server.log_output += record.msg + "\n"
856
Guido van Rossum376e6362003-04-25 14:22:00 +0000857
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000858class LogRecordSocketReceiver(ThreadingTCPServer):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000859
860 """A simple-minded TCP socket-based logging receiver suitable for test
861 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000862
863 allow_reuse_address = 1
Brett Cannon56c4deb2008-03-03 00:38:58 +0000864 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000865
866 def __init__(self, host='localhost',
867 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
868 handler=LogRecordStreamHandler):
869 ThreadingTCPServer.__init__(self, (host, port), handler)
Georg Brandl57826cf2008-02-23 15:06:25 +0000870 self.abort = False
Brett Cannon56c4deb2008-03-03 00:38:58 +0000871 self.timeout = 0.1
872 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000873
874 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000875 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000876 rd, wr, ex = select.select([self.socket.fileno()], [], [],
877 self.timeout)
878 if rd:
879 self.handle_request()
Brett Cannon56c4deb2008-03-03 00:38:58 +0000880 # Notify the main thread that we're about to exit
881 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000882 # close the listen socket
883 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000884
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000885
Victor Stinner6a102812010-04-27 23:55:59 +0000886@unittest.skipUnless(threading, 'Threading required for this test.')
Brett Cannon56c4deb2008-03-03 00:38:58 +0000887class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000888
Brett Cannon56c4deb2008-03-03 00:38:58 +0000889 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000890
Brett Cannon56c4deb2008-03-03 00:38:58 +0000891 def setUp(self):
892 """Set up a TCP server to receive log messages, and a SocketHandler
893 pointing to that server's address and port."""
894 BaseTest.setUp(self)
895 self.tcpserver = LogRecordSocketReceiver(port=0)
896 self.port = self.tcpserver.socket.getsockname()[1]
897 self.threads = [
898 threading.Thread(target=self.tcpserver.serve_until_stopped)]
899 for thread in self.threads:
900 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000901
Brett Cannon56c4deb2008-03-03 00:38:58 +0000902 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
903 self.sock_hdlr.setFormatter(self.root_formatter)
904 self.root_logger.removeHandler(self.root_logger.handlers[0])
905 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000906
Brett Cannon56c4deb2008-03-03 00:38:58 +0000907 def tearDown(self):
908 """Shutdown the TCP server."""
909 try:
910 self.tcpserver.abort = True
911 del self.tcpserver
912 self.root_logger.removeHandler(self.sock_hdlr)
913 self.sock_hdlr.close()
914 for thread in self.threads:
915 thread.join(2.0)
916 finally:
917 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000918
Brett Cannon56c4deb2008-03-03 00:38:58 +0000919 def get_output(self):
920 """Get the log output as received by the TCP server."""
921 # Signal the TCP receiver and wait for it to terminate.
922 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
923 self.tcpserver.finished.wait(2.0)
924 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000925
Brett Cannon56c4deb2008-03-03 00:38:58 +0000926 def test_output(self):
927 # The log message sent to the SocketHandler is properly received.
928 logger = logging.getLogger("tcp")
929 logger.error("spam")
930 logger.debug("eggs")
Ezio Melotti2623a372010-11-21 13:34:58 +0000931 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000932
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000933
Brett Cannon56c4deb2008-03-03 00:38:58 +0000934class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000935
Brett Cannon56c4deb2008-03-03 00:38:58 +0000936 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000937
Brett Cannon56c4deb2008-03-03 00:38:58 +0000938 def setUp(self):
939 """Create a dict to remember potentially destroyed objects."""
940 BaseTest.setUp(self)
941 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000942
Brett Cannon56c4deb2008-03-03 00:38:58 +0000943 def _watch_for_survival(self, *args):
944 """Watch the given objects for survival, by creating weakrefs to
945 them."""
946 for obj in args:
947 key = id(obj), repr(obj)
948 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000949
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000950 def _assertTruesurvival(self):
Brett Cannon56c4deb2008-03-03 00:38:58 +0000951 """Assert that all objects watched for survival have survived."""
952 # Trigger cycle breaking.
953 gc.collect()
954 dead = []
955 for (id_, repr_), ref in self._survivors.items():
956 if ref() is None:
957 dead.append(repr_)
958 if dead:
959 self.fail("%d objects should have survived "
960 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000961
Brett Cannon56c4deb2008-03-03 00:38:58 +0000962 def test_persistent_loggers(self):
963 # Logger objects are persistent and retain their configuration, even
964 # if visible references are destroyed.
965 self.root_logger.setLevel(logging.INFO)
966 foo = logging.getLogger("foo")
967 self._watch_for_survival(foo)
968 foo.setLevel(logging.DEBUG)
969 self.root_logger.debug(self.next_message())
970 foo.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000971 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000972 ('foo', 'DEBUG', '2'),
973 ])
974 del foo
975 # foo has survived.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000976 self._assertTruesurvival()
Brett Cannon56c4deb2008-03-03 00:38:58 +0000977 # foo has retained its settings.
978 bar = logging.getLogger("foo")
979 bar.debug(self.next_message())
Benjamin Peterson4d3f18f2009-07-01 00:36:41 +0000980 self.assert_log_lines([
Brett Cannon56c4deb2008-03-03 00:38:58 +0000981 ('foo', 'DEBUG', '2'),
982 ('foo', 'DEBUG', '3'),
983 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000984
Vinay Sajipb20af942009-02-08 19:06:08 +0000985
Vinay Sajip65d66e12008-09-04 07:31:21 +0000986class EncodingTest(BaseTest):
987 def test_encoding_plain_file(self):
988 # In Python 2.x, a plain file object is treated as having no encoding.
989 log = logging.getLogger("test")
990 fn = tempfile.mktemp(".log")
991 # the non-ascii data we write to the log.
992 data = "foo\x80"
993 try:
994 handler = logging.FileHandler(fn)
995 log.addHandler(handler)
996 try:
997 # write non-ascii data to the log.
998 log.warning(data)
999 finally:
1000 log.removeHandler(handler)
1001 handler.close()
1002 # check we wrote exactly those bytes, ignoring trailing \n etc
1003 f = open(fn)
1004 try:
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001005 self.assertEqual(f.read().rstrip(), data)
Vinay Sajip65d66e12008-09-04 07:31:21 +00001006 finally:
1007 f.close()
1008 finally:
1009 if os.path.isfile(fn):
1010 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001011
Vinay Sajipb20af942009-02-08 19:06:08 +00001012 def test_encoding_cyrillic_unicode(self):
1013 log = logging.getLogger("test")
1014 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1015 message = u'\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1016 #Ensure it's written in a Cyrillic encoding
1017 writer_class = codecs.getwriter('cp1251')
Vinay Sajip74f04502009-04-16 19:07:37 +00001018 writer_class.encoding = 'cp1251'
Vinay Sajipb20af942009-02-08 19:06:08 +00001019 stream = cStringIO.StringIO()
1020 writer = writer_class(stream, 'strict')
1021 handler = logging.StreamHandler(writer)
1022 log.addHandler(handler)
1023 try:
1024 log.warning(message)
1025 finally:
1026 log.removeHandler(handler)
1027 handler.close()
1028 # check we wrote exactly those bytes, ignoring trailing \n etc
1029 s = stream.getvalue()
1030 #Compare against what the data should be when encoded in CP-1251
1031 self.assertEqual(s, '\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1032
1033
Vinay Sajip213faca2008-12-03 23:22:58 +00001034class WarningsTest(BaseTest):
Brett Cannon2da4d622009-04-01 19:57:10 +00001035
Vinay Sajip213faca2008-12-03 23:22:58 +00001036 def test_warnings(self):
Brett Cannon2da4d622009-04-01 19:57:10 +00001037 with warnings.catch_warnings():
Vinay Sajip1e566ce2009-04-05 11:06:24 +00001038 logging.captureWarnings(True)
Brett Cannon2da4d622009-04-01 19:57:10 +00001039 try:
Vinay Sajip14bf0a02009-04-07 17:18:24 +00001040 warnings.filterwarnings("always", category=UserWarning)
Brett Cannon2da4d622009-04-01 19:57:10 +00001041 file = cStringIO.StringIO()
1042 h = logging.StreamHandler(file)
1043 logger = logging.getLogger("py.warnings")
1044 logger.addHandler(h)
1045 warnings.warn("I'm warning you...")
1046 logger.removeHandler(h)
1047 s = file.getvalue()
1048 h.close()
1049 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Vinay Sajip213faca2008-12-03 23:22:58 +00001050
Brett Cannon2da4d622009-04-01 19:57:10 +00001051 #See if an explicit file uses the original implementation
1052 file = cStringIO.StringIO()
1053 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1054 file, "Dummy line")
1055 s = file.getvalue()
1056 file.close()
1057 self.assertEqual(s,
1058 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1059 finally:
1060 logging.captureWarnings(False)
Vinay Sajip213faca2008-12-03 23:22:58 +00001061
Vinay Sajip28c382f2010-02-04 18:48:53 +00001062
1063def formatFunc(format, datefmt=None):
1064 return logging.Formatter(format, datefmt)
1065
1066def handlerFunc():
1067 return logging.StreamHandler()
1068
1069class CustomHandler(logging.StreamHandler):
1070 pass
1071
1072class ConfigDictTest(BaseTest):
1073
1074 """Reading logging config from a dictionary."""
1075
1076 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1077
1078 # config0 is a standard configuration.
1079 config0 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001080 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001081 'formatters': {
1082 'form1' : {
1083 'format' : '%(levelname)s ++ %(message)s',
1084 },
1085 },
1086 'handlers' : {
1087 'hand1' : {
1088 'class' : 'logging.StreamHandler',
1089 'formatter' : 'form1',
1090 'level' : 'NOTSET',
1091 'stream' : 'ext://sys.stdout',
1092 },
1093 },
1094 'root' : {
1095 'level' : 'WARNING',
1096 'handlers' : ['hand1'],
1097 },
1098 }
1099
1100 # config1 adds a little to the standard configuration.
1101 config1 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001102 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001103 'formatters': {
1104 'form1' : {
1105 'format' : '%(levelname)s ++ %(message)s',
1106 },
1107 },
1108 'handlers' : {
1109 'hand1' : {
1110 'class' : 'logging.StreamHandler',
1111 'formatter' : 'form1',
1112 'level' : 'NOTSET',
1113 'stream' : 'ext://sys.stdout',
1114 },
1115 },
1116 'loggers' : {
1117 'compiler.parser' : {
1118 'level' : 'DEBUG',
1119 'handlers' : ['hand1'],
1120 },
1121 },
1122 'root' : {
1123 'level' : 'WARNING',
1124 },
1125 }
1126
1127 # config2 has a subtle configuration error that should be reported
1128 config2 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001129 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001130 'formatters': {
1131 'form1' : {
1132 'format' : '%(levelname)s ++ %(message)s',
1133 },
1134 },
1135 'handlers' : {
1136 'hand1' : {
1137 'class' : 'logging.StreamHandler',
1138 'formatter' : 'form1',
1139 'level' : 'NOTSET',
1140 'stream' : 'ext://sys.stdbout',
1141 },
1142 },
1143 'loggers' : {
1144 'compiler.parser' : {
1145 'level' : 'DEBUG',
1146 'handlers' : ['hand1'],
1147 },
1148 },
1149 'root' : {
1150 'level' : 'WARNING',
1151 },
1152 }
1153
1154 #As config1 but with a misspelt level on a handler
1155 config2a = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001156 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001157 'formatters': {
1158 'form1' : {
1159 'format' : '%(levelname)s ++ %(message)s',
1160 },
1161 },
1162 'handlers' : {
1163 'hand1' : {
1164 'class' : 'logging.StreamHandler',
1165 'formatter' : 'form1',
1166 'level' : 'NTOSET',
1167 'stream' : 'ext://sys.stdout',
1168 },
1169 },
1170 'loggers' : {
1171 'compiler.parser' : {
1172 'level' : 'DEBUG',
1173 'handlers' : ['hand1'],
1174 },
1175 },
1176 'root' : {
1177 'level' : 'WARNING',
1178 },
1179 }
1180
1181
1182 #As config1 but with a misspelt level on a logger
1183 config2b = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001184 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001185 'formatters': {
1186 'form1' : {
1187 'format' : '%(levelname)s ++ %(message)s',
1188 },
1189 },
1190 'handlers' : {
1191 'hand1' : {
1192 'class' : 'logging.StreamHandler',
1193 'formatter' : 'form1',
1194 'level' : 'NOTSET',
1195 'stream' : 'ext://sys.stdout',
1196 },
1197 },
1198 'loggers' : {
1199 'compiler.parser' : {
1200 'level' : 'DEBUG',
1201 'handlers' : ['hand1'],
1202 },
1203 },
1204 'root' : {
1205 'level' : 'WRANING',
1206 },
1207 }
1208
1209 # config3 has a less subtle configuration error
1210 config3 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001211 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001212 'formatters': {
1213 'form1' : {
1214 'format' : '%(levelname)s ++ %(message)s',
1215 },
1216 },
1217 'handlers' : {
1218 'hand1' : {
1219 'class' : 'logging.StreamHandler',
1220 'formatter' : 'misspelled_name',
1221 'level' : 'NOTSET',
1222 'stream' : 'ext://sys.stdout',
1223 },
1224 },
1225 'loggers' : {
1226 'compiler.parser' : {
1227 'level' : 'DEBUG',
1228 'handlers' : ['hand1'],
1229 },
1230 },
1231 'root' : {
1232 'level' : 'WARNING',
1233 },
1234 }
1235
1236 # config4 specifies a custom formatter class to be loaded
1237 config4 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001238 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001239 'formatters': {
1240 'form1' : {
1241 '()' : __name__ + '.ExceptionFormatter',
1242 'format' : '%(levelname)s:%(name)s:%(message)s',
1243 },
1244 },
1245 'handlers' : {
1246 'hand1' : {
1247 'class' : 'logging.StreamHandler',
1248 'formatter' : 'form1',
1249 'level' : 'NOTSET',
1250 'stream' : 'ext://sys.stdout',
1251 },
1252 },
1253 'root' : {
1254 'level' : 'NOTSET',
1255 'handlers' : ['hand1'],
1256 },
1257 }
1258
1259 # As config4 but using an actual callable rather than a string
1260 config4a = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001261 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001262 'formatters': {
1263 'form1' : {
1264 '()' : ExceptionFormatter,
1265 'format' : '%(levelname)s:%(name)s:%(message)s',
1266 },
1267 'form2' : {
1268 '()' : __name__ + '.formatFunc',
1269 'format' : '%(levelname)s:%(name)s:%(message)s',
1270 },
1271 'form3' : {
1272 '()' : formatFunc,
1273 'format' : '%(levelname)s:%(name)s:%(message)s',
1274 },
1275 },
1276 'handlers' : {
1277 'hand1' : {
1278 'class' : 'logging.StreamHandler',
1279 'formatter' : 'form1',
1280 'level' : 'NOTSET',
1281 'stream' : 'ext://sys.stdout',
1282 },
1283 'hand2' : {
1284 '()' : handlerFunc,
1285 },
1286 },
1287 'root' : {
1288 'level' : 'NOTSET',
1289 'handlers' : ['hand1'],
1290 },
1291 }
1292
1293 # config5 specifies a custom handler class to be loaded
1294 config5 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001295 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001296 'formatters': {
1297 'form1' : {
1298 'format' : '%(levelname)s ++ %(message)s',
1299 },
1300 },
1301 'handlers' : {
1302 'hand1' : {
1303 'class' : __name__ + '.CustomHandler',
1304 'formatter' : 'form1',
1305 'level' : 'NOTSET',
1306 'stream' : 'ext://sys.stdout',
1307 },
1308 },
1309 'loggers' : {
1310 'compiler.parser' : {
1311 'level' : 'DEBUG',
1312 'handlers' : ['hand1'],
1313 },
1314 },
1315 'root' : {
1316 'level' : 'WARNING',
1317 },
1318 }
1319
1320 # config6 specifies a custom handler class to be loaded
1321 # but has bad arguments
1322 config6 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001323 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001324 'formatters': {
1325 'form1' : {
1326 'format' : '%(levelname)s ++ %(message)s',
1327 },
1328 },
1329 'handlers' : {
1330 'hand1' : {
1331 'class' : __name__ + '.CustomHandler',
1332 'formatter' : 'form1',
1333 'level' : 'NOTSET',
1334 'stream' : 'ext://sys.stdout',
1335 '9' : 'invalid parameter name',
1336 },
1337 },
1338 'loggers' : {
1339 'compiler.parser' : {
1340 'level' : 'DEBUG',
1341 'handlers' : ['hand1'],
1342 },
1343 },
1344 'root' : {
1345 'level' : 'WARNING',
1346 },
1347 }
1348
1349 #config 7 does not define compiler.parser but defines compiler.lexer
1350 #so compiler.parser should be disabled after applying it
1351 config7 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001352 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001353 'formatters': {
1354 'form1' : {
1355 'format' : '%(levelname)s ++ %(message)s',
1356 },
1357 },
1358 'handlers' : {
1359 'hand1' : {
1360 'class' : 'logging.StreamHandler',
1361 'formatter' : 'form1',
1362 'level' : 'NOTSET',
1363 'stream' : 'ext://sys.stdout',
1364 },
1365 },
1366 'loggers' : {
1367 'compiler.lexer' : {
1368 'level' : 'DEBUG',
1369 'handlers' : ['hand1'],
1370 },
1371 },
1372 'root' : {
1373 'level' : 'WARNING',
1374 },
1375 }
1376
1377 config8 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001378 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001379 'disable_existing_loggers' : False,
1380 'formatters': {
1381 'form1' : {
1382 'format' : '%(levelname)s ++ %(message)s',
1383 },
1384 },
1385 'handlers' : {
1386 'hand1' : {
1387 'class' : 'logging.StreamHandler',
1388 'formatter' : 'form1',
1389 'level' : 'NOTSET',
1390 'stream' : 'ext://sys.stdout',
1391 },
1392 },
1393 'loggers' : {
1394 'compiler' : {
1395 'level' : 'DEBUG',
1396 'handlers' : ['hand1'],
1397 },
1398 'compiler.lexer' : {
1399 },
1400 },
1401 'root' : {
1402 'level' : 'WARNING',
1403 },
1404 }
1405
1406 config9 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001407 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001408 'formatters': {
1409 'form1' : {
1410 'format' : '%(levelname)s ++ %(message)s',
1411 },
1412 },
1413 'handlers' : {
1414 'hand1' : {
1415 'class' : 'logging.StreamHandler',
1416 'formatter' : 'form1',
1417 'level' : 'WARNING',
1418 'stream' : 'ext://sys.stdout',
1419 },
1420 },
1421 'loggers' : {
1422 'compiler.parser' : {
1423 'level' : 'WARNING',
1424 'handlers' : ['hand1'],
1425 },
1426 },
1427 'root' : {
1428 'level' : 'NOTSET',
1429 },
1430 }
1431
1432 config9a = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001433 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001434 'incremental' : True,
1435 'handlers' : {
1436 'hand1' : {
1437 'level' : 'WARNING',
1438 },
1439 },
1440 'loggers' : {
1441 'compiler.parser' : {
1442 'level' : 'INFO',
1443 },
1444 },
1445 }
1446
1447 config9b = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001448 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001449 'incremental' : True,
1450 'handlers' : {
1451 'hand1' : {
1452 'level' : 'INFO',
1453 },
1454 },
1455 'loggers' : {
1456 'compiler.parser' : {
1457 'level' : 'INFO',
1458 },
1459 },
1460 }
1461
1462 #As config1 but with a filter added
1463 config10 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001464 'version': 1,
Vinay Sajip28c382f2010-02-04 18:48:53 +00001465 'formatters': {
1466 'form1' : {
1467 'format' : '%(levelname)s ++ %(message)s',
1468 },
1469 },
1470 'filters' : {
1471 'filt1' : {
1472 'name' : 'compiler.parser',
1473 },
1474 },
1475 'handlers' : {
1476 'hand1' : {
1477 'class' : 'logging.StreamHandler',
1478 'formatter' : 'form1',
1479 'level' : 'NOTSET',
1480 'stream' : 'ext://sys.stdout',
1481 'filters' : ['filt1'],
1482 },
1483 },
1484 'loggers' : {
1485 'compiler.parser' : {
1486 'level' : 'DEBUG',
1487 'filters' : ['filt1'],
1488 },
1489 },
1490 'root' : {
1491 'level' : 'WARNING',
1492 'handlers' : ['hand1'],
1493 },
1494 }
1495
Vinay Sajipf7610b22010-02-04 21:40:56 +00001496 #As config1 but using cfg:// references
1497 config11 = {
Vinay Sajipd45a2782010-03-06 15:12:08 +00001498 'version': 1,
1499 'true_formatters': {
1500 'form1' : {
1501 'format' : '%(levelname)s ++ %(message)s',
1502 },
1503 },
1504 'handler_configs': {
1505 'hand1' : {
1506 'class' : 'logging.StreamHandler',
1507 'formatter' : 'form1',
1508 'level' : 'NOTSET',
1509 'stream' : 'ext://sys.stdout',
1510 },
1511 },
1512 'formatters' : 'cfg://true_formatters',
1513 'handlers' : {
1514 'hand1' : 'cfg://handler_configs[hand1]',
1515 },
1516 'loggers' : {
1517 'compiler.parser' : {
1518 'level' : 'DEBUG',
1519 'handlers' : ['hand1'],
1520 },
1521 },
1522 'root' : {
1523 'level' : 'WARNING',
1524 },
1525 }
1526
1527 #As config11 but missing the version key
1528 config12 = {
1529 'true_formatters': {
1530 'form1' : {
1531 'format' : '%(levelname)s ++ %(message)s',
1532 },
1533 },
1534 'handler_configs': {
1535 'hand1' : {
1536 'class' : 'logging.StreamHandler',
1537 'formatter' : 'form1',
1538 'level' : 'NOTSET',
1539 'stream' : 'ext://sys.stdout',
1540 },
1541 },
1542 'formatters' : 'cfg://true_formatters',
1543 'handlers' : {
1544 'hand1' : 'cfg://handler_configs[hand1]',
1545 },
1546 'loggers' : {
1547 'compiler.parser' : {
1548 'level' : 'DEBUG',
1549 'handlers' : ['hand1'],
1550 },
1551 },
1552 'root' : {
1553 'level' : 'WARNING',
1554 },
1555 }
1556
1557 #As config11 but using an unsupported version
1558 config13 = {
1559 'version': 2,
Vinay Sajipf7610b22010-02-04 21:40:56 +00001560 'true_formatters': {
1561 'form1' : {
1562 'format' : '%(levelname)s ++ %(message)s',
1563 },
1564 },
1565 'handler_configs': {
1566 'hand1' : {
1567 'class' : 'logging.StreamHandler',
1568 'formatter' : 'form1',
1569 'level' : 'NOTSET',
1570 'stream' : 'ext://sys.stdout',
1571 },
1572 },
1573 'formatters' : 'cfg://true_formatters',
1574 'handlers' : {
1575 'hand1' : 'cfg://handler_configs[hand1]',
1576 },
1577 'loggers' : {
1578 'compiler.parser' : {
1579 'level' : 'DEBUG',
1580 'handlers' : ['hand1'],
1581 },
1582 },
1583 'root' : {
1584 'level' : 'WARNING',
1585 },
1586 }
1587
Vinay Sajip28c382f2010-02-04 18:48:53 +00001588 def apply_config(self, conf):
1589 logging.config.dictConfig(conf)
1590
1591 def test_config0_ok(self):
1592 # A simple config which overrides the default settings.
1593 with captured_stdout() as output:
1594 self.apply_config(self.config0)
1595 logger = logging.getLogger()
1596 # Won't output anything
1597 logger.info(self.next_message())
1598 # Outputs a message
1599 logger.error(self.next_message())
1600 self.assert_log_lines([
1601 ('ERROR', '2'),
1602 ], stream=output)
1603 # Original logger output is empty.
1604 self.assert_log_lines([])
1605
1606 def test_config1_ok(self, config=config1):
1607 # A config defining a sub-parser as well.
1608 with captured_stdout() as output:
1609 self.apply_config(config)
1610 logger = logging.getLogger("compiler.parser")
1611 # Both will output a message
1612 logger.info(self.next_message())
1613 logger.error(self.next_message())
1614 self.assert_log_lines([
1615 ('INFO', '1'),
1616 ('ERROR', '2'),
1617 ], stream=output)
1618 # Original logger output is empty.
1619 self.assert_log_lines([])
1620
1621 def test_config2_failure(self):
1622 # A simple config which overrides the default settings.
1623 self.assertRaises(StandardError, self.apply_config, self.config2)
1624
1625 def test_config2a_failure(self):
1626 # A simple config which overrides the default settings.
1627 self.assertRaises(StandardError, self.apply_config, self.config2a)
1628
1629 def test_config2b_failure(self):
1630 # A simple config which overrides the default settings.
1631 self.assertRaises(StandardError, self.apply_config, self.config2b)
1632
1633 def test_config3_failure(self):
1634 # A simple config which overrides the default settings.
1635 self.assertRaises(StandardError, self.apply_config, self.config3)
1636
1637 def test_config4_ok(self):
1638 # A config specifying a custom formatter class.
1639 with captured_stdout() as output:
1640 self.apply_config(self.config4)
1641 #logger = logging.getLogger()
1642 try:
1643 raise RuntimeError()
1644 except RuntimeError:
1645 logging.exception("just testing")
1646 sys.stdout.seek(0)
Ezio Melotti2623a372010-11-21 13:34:58 +00001647 self.assertEqual(output.getvalue(),
Vinay Sajip28c382f2010-02-04 18:48:53 +00001648 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1649 # Original logger output is empty
1650 self.assert_log_lines([])
1651
1652 def test_config4a_ok(self):
1653 # A config specifying a custom formatter class.
1654 with captured_stdout() as output:
1655 self.apply_config(self.config4a)
1656 #logger = logging.getLogger()
1657 try:
1658 raise RuntimeError()
1659 except RuntimeError:
1660 logging.exception("just testing")
1661 sys.stdout.seek(0)
Ezio Melotti2623a372010-11-21 13:34:58 +00001662 self.assertEqual(output.getvalue(),
Vinay Sajip28c382f2010-02-04 18:48:53 +00001663 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1664 # Original logger output is empty
1665 self.assert_log_lines([])
1666
1667 def test_config5_ok(self):
1668 self.test_config1_ok(config=self.config5)
1669
1670 def test_config6_failure(self):
1671 self.assertRaises(StandardError, self.apply_config, self.config6)
1672
1673 def test_config7_ok(self):
1674 with captured_stdout() as output:
1675 self.apply_config(self.config1)
1676 logger = logging.getLogger("compiler.parser")
1677 # Both will output a message
1678 logger.info(self.next_message())
1679 logger.error(self.next_message())
1680 self.assert_log_lines([
1681 ('INFO', '1'),
1682 ('ERROR', '2'),
1683 ], stream=output)
1684 # Original logger output is empty.
1685 self.assert_log_lines([])
1686 with captured_stdout() as output:
1687 self.apply_config(self.config7)
1688 logger = logging.getLogger("compiler.parser")
1689 self.assertTrue(logger.disabled)
1690 logger = logging.getLogger("compiler.lexer")
1691 # Both will output a message
1692 logger.info(self.next_message())
1693 logger.error(self.next_message())
1694 self.assert_log_lines([
1695 ('INFO', '3'),
1696 ('ERROR', '4'),
1697 ], stream=output)
1698 # Original logger output is empty.
1699 self.assert_log_lines([])
1700
1701 #Same as test_config_7_ok but don't disable old loggers.
1702 def test_config_8_ok(self):
1703 with captured_stdout() as output:
1704 self.apply_config(self.config1)
1705 logger = logging.getLogger("compiler.parser")
1706 # Both will output a message
1707 logger.info(self.next_message())
1708 logger.error(self.next_message())
1709 self.assert_log_lines([
1710 ('INFO', '1'),
1711 ('ERROR', '2'),
1712 ], stream=output)
1713 # Original logger output is empty.
1714 self.assert_log_lines([])
1715 with captured_stdout() as output:
1716 self.apply_config(self.config8)
1717 logger = logging.getLogger("compiler.parser")
1718 self.assertFalse(logger.disabled)
1719 # Both will output a message
1720 logger.info(self.next_message())
1721 logger.error(self.next_message())
1722 logger = logging.getLogger("compiler.lexer")
1723 # Both will output a message
1724 logger.info(self.next_message())
1725 logger.error(self.next_message())
1726 self.assert_log_lines([
1727 ('INFO', '3'),
1728 ('ERROR', '4'),
1729 ('INFO', '5'),
1730 ('ERROR', '6'),
1731 ], stream=output)
1732 # Original logger output is empty.
1733 self.assert_log_lines([])
1734
1735 def test_config_9_ok(self):
1736 with captured_stdout() as output:
1737 self.apply_config(self.config9)
1738 logger = logging.getLogger("compiler.parser")
1739 #Nothing will be output since both handler and logger are set to WARNING
1740 logger.info(self.next_message())
1741 self.assert_log_lines([], stream=output)
1742 self.apply_config(self.config9a)
1743 #Nothing will be output since both handler is still set to WARNING
1744 logger.info(self.next_message())
1745 self.assert_log_lines([], stream=output)
1746 self.apply_config(self.config9b)
1747 #Message should now be output
1748 logger.info(self.next_message())
1749 self.assert_log_lines([
1750 ('INFO', '3'),
1751 ], stream=output)
1752
1753 def test_config_10_ok(self):
1754 with captured_stdout() as output:
1755 self.apply_config(self.config10)
1756 logger = logging.getLogger("compiler.parser")
1757 logger.warning(self.next_message())
1758 logger = logging.getLogger('compiler')
1759 #Not output, because filtered
1760 logger.warning(self.next_message())
1761 logger = logging.getLogger('compiler.lexer')
1762 #Not output, because filtered
1763 logger.warning(self.next_message())
1764 logger = logging.getLogger("compiler.parser.codegen")
1765 #Output, as not filtered
1766 logger.error(self.next_message())
1767 self.assert_log_lines([
1768 ('WARNING', '1'),
1769 ('ERROR', '4'),
1770 ], stream=output)
1771
Vinay Sajipf7610b22010-02-04 21:40:56 +00001772 def test_config11_ok(self):
1773 self.test_config1_ok(self.config11)
1774
Vinay Sajipd45a2782010-03-06 15:12:08 +00001775 def test_config12_failure(self):
1776 self.assertRaises(StandardError, self.apply_config, self.config12)
1777
1778 def test_config13_failure(self):
1779 self.assertRaises(StandardError, self.apply_config, self.config13)
1780
Victor Stinner6a102812010-04-27 23:55:59 +00001781 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajip28c382f2010-02-04 18:48:53 +00001782 def setup_via_listener(self, text):
Vinay Sajip27a13702010-05-03 15:11:53 +00001783 # Ask for a randomly assigned port (by using port 0)
1784 t = logging.config.listen(0)
Vinay Sajip28c382f2010-02-04 18:48:53 +00001785 t.start()
Benjamin Peterson239f1382010-02-06 22:08:15 +00001786 t.ready.wait()
Vinay Sajip27a13702010-05-03 15:11:53 +00001787 # Now get the port allocated
1788 port = t.port
Vinay Sajip9a164ac2010-02-08 16:05:50 +00001789 t.ready.clear()
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001790 try:
1791 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Vinay Sajip23739262010-02-05 23:43:11 +00001792 sock.settimeout(2.0)
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001793 sock.connect(('localhost', port))
Vinay Sajip28c382f2010-02-04 18:48:53 +00001794
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001795 slen = struct.pack('>L', len(text))
1796 s = slen + text
1797 sentsofar = 0
1798 left = len(s)
1799 while left > 0:
1800 sent = sock.send(s[sentsofar:])
1801 sentsofar += sent
1802 left -= sent
1803 sock.close()
1804 finally:
Vinay Sajip9a164ac2010-02-08 16:05:50 +00001805 t.ready.wait(2.0)
Vinay Sajip3dd734f2010-02-05 14:52:05 +00001806 logging.config.stopListening()
Vinay Sajip23739262010-02-05 23:43:11 +00001807 t.join(2.0)
Vinay Sajip28c382f2010-02-04 18:48:53 +00001808
Vinay Sajip23739262010-02-05 23:43:11 +00001809 def test_listen_config_10_ok(self):
Vinay Sajip28c382f2010-02-04 18:48:53 +00001810 with captured_stdout() as output:
1811 self.setup_via_listener(json.dumps(self.config10))
1812 logger = logging.getLogger("compiler.parser")
1813 logger.warning(self.next_message())
1814 logger = logging.getLogger('compiler')
1815 #Not output, because filtered
1816 logger.warning(self.next_message())
1817 logger = logging.getLogger('compiler.lexer')
1818 #Not output, because filtered
1819 logger.warning(self.next_message())
1820 logger = logging.getLogger("compiler.parser.codegen")
1821 #Output, as not filtered
1822 logger.error(self.next_message())
1823 self.assert_log_lines([
1824 ('WARNING', '1'),
1825 ('ERROR', '4'),
1826 ], stream=output)
1827
Vinay Sajip23739262010-02-05 23:43:11 +00001828 def test_listen_config_1_ok(self):
Vinay Sajip28c382f2010-02-04 18:48:53 +00001829 with captured_stdout() as output:
1830 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
1831 logger = logging.getLogger("compiler.parser")
1832 # Both will output a message
1833 logger.info(self.next_message())
1834 logger.error(self.next_message())
1835 self.assert_log_lines([
1836 ('INFO', '1'),
1837 ('ERROR', '2'),
1838 ], stream=output)
1839 # Original logger output is empty.
1840 self.assert_log_lines([])
1841
1842
Vinay Sajip657514a2010-02-07 01:37:08 +00001843class ManagerTest(BaseTest):
1844 def test_manager_loggerclass(self):
1845 logged = []
1846
1847 class MyLogger(logging.Logger):
1848 def _log(self, level, msg, args, exc_info=None, extra=None):
1849 logged.append(msg)
1850
1851 man = logging.Manager(None)
1852 self.assertRaises(TypeError, man.setLoggerClass, int)
1853 man.setLoggerClass(MyLogger)
1854 logger = man.getLogger('test')
Vinay Sajip657514a2010-02-07 01:37:08 +00001855 logger.warning('should appear in logged')
1856 logging.warning('should not appear in logged')
1857
1858 self.assertEqual(logged, ['should appear in logged'])
1859
1860
Vinay Sajip804899b2010-03-22 15:29:01 +00001861class ChildLoggerTest(BaseTest):
1862 def test_child_loggers(self):
1863 r = logging.getLogger()
1864 l1 = logging.getLogger('abc')
1865 l2 = logging.getLogger('def.ghi')
1866 c1 = r.getChild('xyz')
1867 c2 = r.getChild('uvw.xyz')
1868 self.assertTrue(c1 is logging.getLogger('xyz'))
1869 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
1870 c1 = l1.getChild('def')
1871 c2 = c1.getChild('ghi')
1872 c3 = l1.getChild('def.ghi')
1873 self.assertTrue(c1 is logging.getLogger('abc.def'))
1874 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
1875 self.assertTrue(c2 is c3)
1876
1877
Vinay Sajip885f6fd2012-04-24 23:15:10 +01001878class HandlerTest(BaseTest):
1879
Vinay Sajipc3ea4082012-05-31 12:35:13 +01001880 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajip885f6fd2012-04-24 23:15:10 +01001881 @unittest.skipUnless(threading, 'Threading required for this test.')
1882 def test_race(self):
1883 # Issue #14632 refers.
1884 def remove_loop(fname, tries):
1885 for _ in range(tries):
1886 try:
1887 os.unlink(fname)
1888 except OSError:
1889 pass
1890 time.sleep(0.004 * random.randint(0, 4))
1891
Vinay Sajip00086bb2012-04-25 10:45:27 +01001892 del_count = 500
1893 log_count = 500
Vinay Sajip885f6fd2012-04-24 23:15:10 +01001894
Vinay Sajip885f6fd2012-04-24 23:15:10 +01001895 for delay in (False, True):
Vinay Sajip00086bb2012-04-25 10:45:27 +01001896 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
1897 os.close(fd)
1898 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
1899 remover.daemon = True
1900 remover.start()
Vinay Sajip885f6fd2012-04-24 23:15:10 +01001901 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajip885f6fd2012-04-24 23:15:10 +01001902 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
1903 h.setFormatter(f)
Vinay Sajip00086bb2012-04-25 10:45:27 +01001904 try:
1905 for _ in range(log_count):
1906 time.sleep(0.005)
1907 r = logging.makeLogRecord({'msg': 'testing' })
1908 h.handle(r)
1909 finally:
Vinay Sajip00086bb2012-04-25 10:45:27 +01001910 remover.join()
Vinay Sajip7b577502012-05-19 16:50:07 +01001911 try:
1912 h.close()
1913 except ValueError:
1914 pass
Vinay Sajip00086bb2012-04-25 10:45:27 +01001915 if os.path.exists(fn):
1916 os.unlink(fn)
Vinay Sajip885f6fd2012-04-24 23:15:10 +01001917
1918
Brett Cannon56c4deb2008-03-03 00:38:58 +00001919# Set the locale to the platform-dependent default. I have no idea
1920# why the test does this, but in any case we save the current locale
1921# first and restore it at the end.
1922@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00001923def test_main():
Brett Cannon56c4deb2008-03-03 00:38:58 +00001924 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip657514a2010-02-07 01:37:08 +00001925 CustomLevelsAndFiltersTest, MemoryHandlerTest,
1926 ConfigFileTest, SocketHandlerTest, MemoryTest,
Vinay Sajip804899b2010-03-22 15:29:01 +00001927 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajip885f6fd2012-04-24 23:15:10 +01001928 ChildLoggerTest, HandlerTest)
Jeremy Hylton096d9862003-07-18 03:19:20 +00001929
Brett Cannon56c4deb2008-03-03 00:38:58 +00001930if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001931 test_main()