blob: ca4d9309f27c43de804a4d7209ae6e5b82e13c3d [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajip66b8b082012-04-24 23:25:30 +01003# Copyright 2001-2012 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00004#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19"""Test harness for the logging module. Run all tests.
20
Vinay Sajip66b8b082012-04-24 23:25:30 +010021Copyright (C) 2001-2012 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Christian Heimes180510d2008-03-03 19:15:45 +000024import logging
25import logging.handlers
26import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000027
Benjamin Petersonf91df042009-02-13 02:50:59 +000028import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Christian Heimes180510d2008-03-03 19:15:45 +000030import pickle
31import io
32import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000033import json
Christian Heimes180510d2008-03-03 19:15:45 +000034import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000035import queue
Vinay Sajip66b8b082012-04-24 23:25:30 +010036import random
Christian Heimes180510d2008-03-03 19:15:45 +000037import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000038import select
Christian Heimes180510d2008-03-03 19:15:45 +000039import socket
Alexandre Vassalottice261952008-05-12 02:31:37 +000040from socketserver import ThreadingTCPServer, StreamRequestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000041import struct
42import sys
43import tempfile
Florent Xiclunadc692742010-08-15 20:16:27 +000044from test.support import captured_stdout, run_with_locale, run_unittest
Vinay Sajipe723e962011-04-15 22:27:17 +010045from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000046import textwrap
Vinay Sajip66b8b082012-04-24 23:25:30 +010047import time
Christian Heimes180510d2008-03-03 19:15:45 +000048import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000049import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000050import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000051try:
52 import threading
53except ImportError:
54 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000055
56
Christian Heimes180510d2008-03-03 19:15:45 +000057class BaseTest(unittest.TestCase):
58
59 """Base class for logging tests."""
60
61 log_format = "%(name)s -> %(levelname)s: %(message)s"
62 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
63 message_num = 0
64
65 def setUp(self):
66 """Setup the default logging stream to an internal StringIO instance,
67 so that we can examine log output as we want."""
68 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000069 logging._acquireLock()
70 try:
Christian Heimes180510d2008-03-03 19:15:45 +000071 self.saved_handlers = logging._handlers.copy()
72 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000073 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000074 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000075 self.logger_states = logger_states = {}
76 for name in saved_loggers:
77 logger_states[name] = getattr(saved_loggers[name],
78 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000079 finally:
80 logging._releaseLock()
81
Benjamin Peterson22005fc2010-04-11 16:25:06 +000082 # Set two unused loggers: one non-ASCII and one Unicode.
83 # This is to test correct operation when sorting existing
84 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000085 self.logger1 = logging.getLogger("\xab\xd7\xbb")
86 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000087
Christian Heimes180510d2008-03-03 19:15:45 +000088 self.root_logger = logging.getLogger("")
89 self.original_logging_level = self.root_logger.getEffectiveLevel()
90
91 self.stream = io.StringIO()
92 self.root_logger.setLevel(logging.DEBUG)
93 self.root_hdlr = logging.StreamHandler(self.stream)
94 self.root_formatter = logging.Formatter(self.log_format)
95 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +000096 if self.logger1.hasHandlers():
97 hlist = self.logger1.handlers + self.root_logger.handlers
98 raise AssertionError('Unexpected handlers: %s' % hlist)
99 if self.logger2.hasHandlers():
100 hlist = self.logger2.handlers + self.root_logger.handlers
101 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000102 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000103 self.assertTrue(self.logger1.hasHandlers())
104 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000105
106 def tearDown(self):
107 """Remove our logging stream, and restore the original logging
108 level."""
109 self.stream.close()
110 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000111 while self.root_logger.handlers:
112 h = self.root_logger.handlers[0]
113 self.root_logger.removeHandler(h)
114 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000115 self.root_logger.setLevel(self.original_logging_level)
116 logging._acquireLock()
117 try:
118 logging._levelNames.clear()
119 logging._levelNames.update(self.saved_level_names)
120 logging._handlers.clear()
121 logging._handlers.update(self.saved_handlers)
122 logging._handlerList[:] = self.saved_handler_list
123 loggerDict = logging.getLogger().manager.loggerDict
124 loggerDict.clear()
125 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000126 logger_states = self.logger_states
127 for name in self.logger_states:
128 if logger_states[name] is not None:
129 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000130 finally:
131 logging._releaseLock()
132
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000133 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000134 """Match the collected log lines against the regular expression
135 self.expected_log_pat, and compare the extracted group values to
136 the expected_values list of tuples."""
137 stream = stream or self.stream
138 pat = re.compile(self.expected_log_pat)
139 try:
140 stream.reset()
141 actual_lines = stream.readlines()
142 except AttributeError:
143 # StringIO.StringIO lacks a reset() method.
144 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000145 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000146 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000147 for actual, expected in zip(actual_lines, expected_values):
148 match = pat.search(actual)
149 if not match:
150 self.fail("Log line does not match expected pattern:\n" +
151 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000152 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000153 s = stream.read()
154 if s:
155 self.fail("Remaining output at end of log stream:\n" + s)
156
157 def next_message(self):
158 """Generate a message consisting solely of an auto-incrementing
159 integer."""
160 self.message_num += 1
161 return "%d" % self.message_num
162
163
164class BuiltinLevelsTest(BaseTest):
165 """Test builtin levels and their inheritance."""
166
167 def test_flat(self):
168 #Logging levels in a flat logger namespace.
169 m = self.next_message
170
171 ERR = logging.getLogger("ERR")
172 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000173 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000174 INF.setLevel(logging.INFO)
175 DEB = logging.getLogger("DEB")
176 DEB.setLevel(logging.DEBUG)
177
178 # These should log.
179 ERR.log(logging.CRITICAL, m())
180 ERR.error(m())
181
182 INF.log(logging.CRITICAL, m())
183 INF.error(m())
184 INF.warn(m())
185 INF.info(m())
186
187 DEB.log(logging.CRITICAL, m())
188 DEB.error(m())
189 DEB.warn (m())
190 DEB.info (m())
191 DEB.debug(m())
192
193 # These should not log.
194 ERR.warn(m())
195 ERR.info(m())
196 ERR.debug(m())
197
198 INF.debug(m())
199
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000200 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000201 ('ERR', 'CRITICAL', '1'),
202 ('ERR', 'ERROR', '2'),
203 ('INF', 'CRITICAL', '3'),
204 ('INF', 'ERROR', '4'),
205 ('INF', 'WARNING', '5'),
206 ('INF', 'INFO', '6'),
207 ('DEB', 'CRITICAL', '7'),
208 ('DEB', 'ERROR', '8'),
209 ('DEB', 'WARNING', '9'),
210 ('DEB', 'INFO', '10'),
211 ('DEB', 'DEBUG', '11'),
212 ])
213
214 def test_nested_explicit(self):
215 # Logging levels in a nested namespace, all explicitly set.
216 m = self.next_message
217
218 INF = logging.getLogger("INF")
219 INF.setLevel(logging.INFO)
220 INF_ERR = logging.getLogger("INF.ERR")
221 INF_ERR.setLevel(logging.ERROR)
222
223 # These should log.
224 INF_ERR.log(logging.CRITICAL, m())
225 INF_ERR.error(m())
226
227 # These should not log.
228 INF_ERR.warn(m())
229 INF_ERR.info(m())
230 INF_ERR.debug(m())
231
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000232 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000233 ('INF.ERR', 'CRITICAL', '1'),
234 ('INF.ERR', 'ERROR', '2'),
235 ])
236
237 def test_nested_inherited(self):
238 #Logging levels in a nested namespace, inherited from parent loggers.
239 m = self.next_message
240
241 INF = logging.getLogger("INF")
242 INF.setLevel(logging.INFO)
243 INF_ERR = logging.getLogger("INF.ERR")
244 INF_ERR.setLevel(logging.ERROR)
245 INF_UNDEF = logging.getLogger("INF.UNDEF")
246 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
247 UNDEF = logging.getLogger("UNDEF")
248
249 # These should log.
250 INF_UNDEF.log(logging.CRITICAL, m())
251 INF_UNDEF.error(m())
252 INF_UNDEF.warn(m())
253 INF_UNDEF.info(m())
254 INF_ERR_UNDEF.log(logging.CRITICAL, m())
255 INF_ERR_UNDEF.error(m())
256
257 # These should not log.
258 INF_UNDEF.debug(m())
259 INF_ERR_UNDEF.warn(m())
260 INF_ERR_UNDEF.info(m())
261 INF_ERR_UNDEF.debug(m())
262
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000263 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000264 ('INF.UNDEF', 'CRITICAL', '1'),
265 ('INF.UNDEF', 'ERROR', '2'),
266 ('INF.UNDEF', 'WARNING', '3'),
267 ('INF.UNDEF', 'INFO', '4'),
268 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
269 ('INF.ERR.UNDEF', 'ERROR', '6'),
270 ])
271
272 def test_nested_with_virtual_parent(self):
273 # Logging levels when some parent does not exist yet.
274 m = self.next_message
275
276 INF = logging.getLogger("INF")
277 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
278 CHILD = logging.getLogger("INF.BADPARENT")
279 INF.setLevel(logging.INFO)
280
281 # These should log.
282 GRANDCHILD.log(logging.FATAL, m())
283 GRANDCHILD.info(m())
284 CHILD.log(logging.FATAL, m())
285 CHILD.info(m())
286
287 # These should not log.
288 GRANDCHILD.debug(m())
289 CHILD.debug(m())
290
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000291 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000292 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
293 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
294 ('INF.BADPARENT', 'CRITICAL', '3'),
295 ('INF.BADPARENT', 'INFO', '4'),
296 ])
297
Vinay Sajip61b787e2011-11-07 08:53:03 +0000298 def test_invalid_name(self):
Vinay Sajip3bd56382011-11-07 10:15:08 +0000299 self.assertRaises(TypeError, logging.getLogger, any)
Christian Heimes180510d2008-03-03 19:15:45 +0000300
301class BasicFilterTest(BaseTest):
302
303 """Test the bundled Filter class."""
304
305 def test_filter(self):
306 # Only messages satisfying the specified criteria pass through the
307 # filter.
308 filter_ = logging.Filter("spam.eggs")
309 handler = self.root_logger.handlers[0]
310 try:
311 handler.addFilter(filter_)
312 spam = logging.getLogger("spam")
313 spam_eggs = logging.getLogger("spam.eggs")
314 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
315 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
316
317 spam.info(self.next_message())
318 spam_eggs.info(self.next_message()) # Good.
319 spam_eggs_fish.info(self.next_message()) # Good.
320 spam_bakedbeans.info(self.next_message())
321
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000322 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000323 ('spam.eggs', 'INFO', '2'),
324 ('spam.eggs.fish', 'INFO', '3'),
325 ])
326 finally:
327 handler.removeFilter(filter_)
328
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000329 def test_callable_filter(self):
330 # Only messages satisfying the specified criteria pass through the
331 # filter.
332
333 def filterfunc(record):
334 parts = record.name.split('.')
335 prefix = '.'.join(parts[:2])
336 return prefix == 'spam.eggs'
337
338 handler = self.root_logger.handlers[0]
339 try:
340 handler.addFilter(filterfunc)
341 spam = logging.getLogger("spam")
342 spam_eggs = logging.getLogger("spam.eggs")
343 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
344 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
345
346 spam.info(self.next_message())
347 spam_eggs.info(self.next_message()) # Good.
348 spam_eggs_fish.info(self.next_message()) # Good.
349 spam_bakedbeans.info(self.next_message())
350
351 self.assert_log_lines([
352 ('spam.eggs', 'INFO', '2'),
353 ('spam.eggs.fish', 'INFO', '3'),
354 ])
355 finally:
356 handler.removeFilter(filterfunc)
357
Christian Heimes180510d2008-03-03 19:15:45 +0000358
359#
360# First, we define our levels. There can be as many as you want - the only
361# limitations are that they should be integers, the lowest should be > 0 and
362# larger values mean less information being logged. If you need specific
363# level values which do not fit into these limitations, you can use a
364# mapping dictionary to convert between your application levels and the
365# logging system.
366#
367SILENT = 120
368TACITURN = 119
369TERSE = 118
370EFFUSIVE = 117
371SOCIABLE = 116
372VERBOSE = 115
373TALKATIVE = 114
374GARRULOUS = 113
375CHATTERBOX = 112
376BORING = 111
377
378LEVEL_RANGE = range(BORING, SILENT + 1)
379
380#
381# Next, we define names for our levels. You don't need to do this - in which
382# case the system will use "Level n" to denote the text for the level.
383#
384my_logging_levels = {
385 SILENT : 'Silent',
386 TACITURN : 'Taciturn',
387 TERSE : 'Terse',
388 EFFUSIVE : 'Effusive',
389 SOCIABLE : 'Sociable',
390 VERBOSE : 'Verbose',
391 TALKATIVE : 'Talkative',
392 GARRULOUS : 'Garrulous',
393 CHATTERBOX : 'Chatterbox',
394 BORING : 'Boring',
395}
396
397class GarrulousFilter(logging.Filter):
398
399 """A filter which blocks garrulous messages."""
400
401 def filter(self, record):
402 return record.levelno != GARRULOUS
403
404class VerySpecificFilter(logging.Filter):
405
406 """A filter which blocks sociable and taciturn messages."""
407
408 def filter(self, record):
409 return record.levelno not in [SOCIABLE, TACITURN]
410
411
412class CustomLevelsAndFiltersTest(BaseTest):
413
414 """Test various filtering possibilities with custom logging levels."""
415
416 # Skip the logger name group.
417 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
418
419 def setUp(self):
420 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000421 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000422 logging.addLevelName(k, v)
423
424 def log_at_all_levels(self, logger):
425 for lvl in LEVEL_RANGE:
426 logger.log(lvl, self.next_message())
427
428 def test_logger_filter(self):
429 # Filter at logger level.
430 self.root_logger.setLevel(VERBOSE)
431 # Levels >= 'Verbose' are good.
432 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000433 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000434 ('Verbose', '5'),
435 ('Sociable', '6'),
436 ('Effusive', '7'),
437 ('Terse', '8'),
438 ('Taciturn', '9'),
439 ('Silent', '10'),
440 ])
441
442 def test_handler_filter(self):
443 # Filter at handler level.
444 self.root_logger.handlers[0].setLevel(SOCIABLE)
445 try:
446 # Levels >= 'Sociable' are good.
447 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000448 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000449 ('Sociable', '6'),
450 ('Effusive', '7'),
451 ('Terse', '8'),
452 ('Taciturn', '9'),
453 ('Silent', '10'),
454 ])
455 finally:
456 self.root_logger.handlers[0].setLevel(logging.NOTSET)
457
458 def test_specific_filters(self):
459 # Set a specific filter object on the handler, and then add another
460 # filter object on the logger itself.
461 handler = self.root_logger.handlers[0]
462 specific_filter = None
463 garr = GarrulousFilter()
464 handler.addFilter(garr)
465 try:
466 self.log_at_all_levels(self.root_logger)
467 first_lines = [
468 # Notice how 'Garrulous' is missing
469 ('Boring', '1'),
470 ('Chatterbox', '2'),
471 ('Talkative', '4'),
472 ('Verbose', '5'),
473 ('Sociable', '6'),
474 ('Effusive', '7'),
475 ('Terse', '8'),
476 ('Taciturn', '9'),
477 ('Silent', '10'),
478 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000479 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000480
481 specific_filter = VerySpecificFilter()
482 self.root_logger.addFilter(specific_filter)
483 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000484 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000485 # Not only 'Garrulous' is still missing, but also 'Sociable'
486 # and 'Taciturn'
487 ('Boring', '11'),
488 ('Chatterbox', '12'),
489 ('Talkative', '14'),
490 ('Verbose', '15'),
491 ('Effusive', '17'),
492 ('Terse', '18'),
493 ('Silent', '20'),
494 ])
495 finally:
496 if specific_filter:
497 self.root_logger.removeFilter(specific_filter)
498 handler.removeFilter(garr)
499
500
501class MemoryHandlerTest(BaseTest):
502
503 """Tests for the MemoryHandler."""
504
505 # Do not bother with a logger name group.
506 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
507
508 def setUp(self):
509 BaseTest.setUp(self)
510 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
511 self.root_hdlr)
512 self.mem_logger = logging.getLogger('mem')
513 self.mem_logger.propagate = 0
514 self.mem_logger.addHandler(self.mem_hdlr)
515
516 def tearDown(self):
517 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000518 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000519
520 def test_flush(self):
521 # The memory handler flushes to its target handler based on specific
522 # criteria (message count and message level).
523 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000524 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000525 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000526 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000527 # This will flush because the level is >= logging.WARNING
528 self.mem_logger.warn(self.next_message())
529 lines = [
530 ('DEBUG', '1'),
531 ('INFO', '2'),
532 ('WARNING', '3'),
533 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000534 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000535 for n in (4, 14):
536 for i in range(9):
537 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000538 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000539 # This will flush because it's the 10th message since the last
540 # flush.
541 self.mem_logger.debug(self.next_message())
542 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000543 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000544
545 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000546 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000547
548
549class ExceptionFormatter(logging.Formatter):
550 """A special exception formatter."""
551 def formatException(self, ei):
552 return "Got a [%s]" % ei[0].__name__
553
554
555class ConfigFileTest(BaseTest):
556
557 """Reading logging config from a .ini-style config file."""
558
559 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
560
561 # config0 is a standard configuration.
562 config0 = """
563 [loggers]
564 keys=root
565
566 [handlers]
567 keys=hand1
568
569 [formatters]
570 keys=form1
571
572 [logger_root]
573 level=WARNING
574 handlers=hand1
575
576 [handler_hand1]
577 class=StreamHandler
578 level=NOTSET
579 formatter=form1
580 args=(sys.stdout,)
581
582 [formatter_form1]
583 format=%(levelname)s ++ %(message)s
584 datefmt=
585 """
586
587 # config1 adds a little to the standard configuration.
588 config1 = """
589 [loggers]
590 keys=root,parser
591
592 [handlers]
593 keys=hand1
594
595 [formatters]
596 keys=form1
597
598 [logger_root]
599 level=WARNING
600 handlers=
601
602 [logger_parser]
603 level=DEBUG
604 handlers=hand1
605 propagate=1
606 qualname=compiler.parser
607
608 [handler_hand1]
609 class=StreamHandler
610 level=NOTSET
611 formatter=form1
612 args=(sys.stdout,)
613
614 [formatter_form1]
615 format=%(levelname)s ++ %(message)s
616 datefmt=
617 """
618
Vinay Sajip3f84b072011-03-07 17:49:33 +0000619 # config1a moves the handler to the root.
620 config1a = """
621 [loggers]
622 keys=root,parser
623
624 [handlers]
625 keys=hand1
626
627 [formatters]
628 keys=form1
629
630 [logger_root]
631 level=WARNING
632 handlers=hand1
633
634 [logger_parser]
635 level=DEBUG
636 handlers=
637 propagate=1
638 qualname=compiler.parser
639
640 [handler_hand1]
641 class=StreamHandler
642 level=NOTSET
643 formatter=form1
644 args=(sys.stdout,)
645
646 [formatter_form1]
647 format=%(levelname)s ++ %(message)s
648 datefmt=
649 """
650
Christian Heimes180510d2008-03-03 19:15:45 +0000651 # config2 has a subtle configuration error that should be reported
652 config2 = config1.replace("sys.stdout", "sys.stbout")
653
654 # config3 has a less subtle configuration error
655 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
656
657 # config4 specifies a custom formatter class to be loaded
658 config4 = """
659 [loggers]
660 keys=root
661
662 [handlers]
663 keys=hand1
664
665 [formatters]
666 keys=form1
667
668 [logger_root]
669 level=NOTSET
670 handlers=hand1
671
672 [handler_hand1]
673 class=StreamHandler
674 level=NOTSET
675 formatter=form1
676 args=(sys.stdout,)
677
678 [formatter_form1]
679 class=""" + __name__ + """.ExceptionFormatter
680 format=%(levelname)s:%(name)s:%(message)s
681 datefmt=
682 """
683
Georg Brandl3dbca812008-07-23 16:10:53 +0000684 # config5 specifies a custom handler class to be loaded
685 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
686
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000687 # config6 uses ', ' delimiters in the handlers and formatters sections
688 config6 = """
689 [loggers]
690 keys=root,parser
691
692 [handlers]
693 keys=hand1, hand2
694
695 [formatters]
696 keys=form1, form2
697
698 [logger_root]
699 level=WARNING
700 handlers=
701
702 [logger_parser]
703 level=DEBUG
704 handlers=hand1
705 propagate=1
706 qualname=compiler.parser
707
708 [handler_hand1]
709 class=StreamHandler
710 level=NOTSET
711 formatter=form1
712 args=(sys.stdout,)
713
714 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000715 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000716 level=NOTSET
717 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000718 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000719
720 [formatter_form1]
721 format=%(levelname)s ++ %(message)s
722 datefmt=
723
724 [formatter_form2]
725 format=%(message)s
726 datefmt=
727 """
728
Vinay Sajip3f84b072011-03-07 17:49:33 +0000729 # config7 adds a compiler logger.
730 config7 = """
731 [loggers]
732 keys=root,parser,compiler
733
734 [handlers]
735 keys=hand1
736
737 [formatters]
738 keys=form1
739
740 [logger_root]
741 level=WARNING
742 handlers=hand1
743
744 [logger_compiler]
745 level=DEBUG
746 handlers=
747 propagate=1
748 qualname=compiler
749
750 [logger_parser]
751 level=DEBUG
752 handlers=
753 propagate=1
754 qualname=compiler.parser
755
756 [handler_hand1]
757 class=StreamHandler
758 level=NOTSET
759 formatter=form1
760 args=(sys.stdout,)
761
762 [formatter_form1]
763 format=%(levelname)s ++ %(message)s
764 datefmt=
765 """
766
Christian Heimes180510d2008-03-03 19:15:45 +0000767 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000768 file = io.StringIO(textwrap.dedent(conf))
769 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000770
771 def test_config0_ok(self):
772 # A simple config file which overrides the default settings.
773 with captured_stdout() as output:
774 self.apply_config(self.config0)
775 logger = logging.getLogger()
776 # Won't output anything
777 logger.info(self.next_message())
778 # Outputs a message
779 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000780 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000781 ('ERROR', '2'),
782 ], stream=output)
783 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000784 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000785
Georg Brandl3dbca812008-07-23 16:10:53 +0000786 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000787 # A config file defining a sub-parser as well.
788 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000789 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000790 logger = logging.getLogger("compiler.parser")
791 # Both will output a message
792 logger.info(self.next_message())
793 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000794 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000795 ('INFO', '1'),
796 ('ERROR', '2'),
797 ], stream=output)
798 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000799 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000800
801 def test_config2_failure(self):
802 # A simple config file which overrides the default settings.
803 self.assertRaises(Exception, self.apply_config, self.config2)
804
805 def test_config3_failure(self):
806 # A simple config file which overrides the default settings.
807 self.assertRaises(Exception, self.apply_config, self.config3)
808
809 def test_config4_ok(self):
810 # A config file specifying a custom formatter class.
811 with captured_stdout() as output:
812 self.apply_config(self.config4)
813 logger = logging.getLogger()
814 try:
815 raise RuntimeError()
816 except RuntimeError:
817 logging.exception("just testing")
818 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000819 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000820 "ERROR:root:just testing\nGot a [RuntimeError]\n")
821 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000822 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000823
Georg Brandl3dbca812008-07-23 16:10:53 +0000824 def test_config5_ok(self):
825 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000826
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000827 def test_config6_ok(self):
828 self.test_config1_ok(config=self.config6)
829
Vinay Sajip3f84b072011-03-07 17:49:33 +0000830 def test_config7_ok(self):
831 with captured_stdout() as output:
832 self.apply_config(self.config1a)
833 logger = logging.getLogger("compiler.parser")
834 # See issue #11424. compiler-hyphenated sorts
835 # between compiler and compiler.xyz and this
836 # was preventing compiler.xyz from being included
837 # in the child loggers of compiler because of an
838 # overzealous loop termination condition.
839 hyphenated = logging.getLogger('compiler-hyphenated')
840 # All will output a message
841 logger.info(self.next_message())
842 logger.error(self.next_message())
843 hyphenated.critical(self.next_message())
844 self.assert_log_lines([
845 ('INFO', '1'),
846 ('ERROR', '2'),
847 ('CRITICAL', '3'),
848 ], stream=output)
849 # Original logger output is empty.
850 self.assert_log_lines([])
851 with captured_stdout() as output:
852 self.apply_config(self.config7)
853 logger = logging.getLogger("compiler.parser")
854 self.assertFalse(logger.disabled)
855 # Both will output a message
856 logger.info(self.next_message())
857 logger.error(self.next_message())
858 logger = logging.getLogger("compiler.lexer")
859 # Both will output a message
860 logger.info(self.next_message())
861 logger.error(self.next_message())
862 # Will not appear
863 hyphenated.critical(self.next_message())
864 self.assert_log_lines([
865 ('INFO', '4'),
866 ('ERROR', '5'),
867 ('INFO', '6'),
868 ('ERROR', '7'),
869 ], stream=output)
870 # Original logger output is empty.
871 self.assert_log_lines([])
872
Christian Heimes180510d2008-03-03 19:15:45 +0000873class LogRecordStreamHandler(StreamRequestHandler):
874
875 """Handler for a streaming logging request. It saves the log message in the
876 TCP server's 'log_output' attribute."""
877
878 TCP_LOG_END = "!!!END!!!"
879
880 def handle(self):
881 """Handle multiple requests - each expected to be of 4-byte length,
882 followed by the LogRecord in pickle format. Logs the record
883 according to whatever policy is configured locally."""
884 while True:
885 chunk = self.connection.recv(4)
886 if len(chunk) < 4:
887 break
888 slen = struct.unpack(">L", chunk)[0]
889 chunk = self.connection.recv(slen)
890 while len(chunk) < slen:
891 chunk = chunk + self.connection.recv(slen - len(chunk))
892 obj = self.unpickle(chunk)
893 record = logging.makeLogRecord(obj)
894 self.handle_log_record(record)
895
896 def unpickle(self, data):
897 return pickle.loads(data)
898
899 def handle_log_record(self, record):
900 # If the end-of-messages sentinel is seen, tell the server to
901 # terminate.
902 if self.TCP_LOG_END in record.msg:
903 self.server.abort = 1
904 return
905 self.server.log_output += record.msg + "\n"
906
Guido van Rossum376e6362003-04-25 14:22:00 +0000907
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000908class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +0000909
910 """A simple-minded TCP socket-based logging receiver suitable for test
911 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000912
913 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +0000914 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000915
916 def __init__(self, host='localhost',
917 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
918 handler=LogRecordStreamHandler):
919 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +0000920 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +0000921 self.timeout = 0.1
922 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000923
924 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000925 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000926 rd, wr, ex = select.select([self.socket.fileno()], [], [],
927 self.timeout)
928 if rd:
929 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +0000930 # Notify the main thread that we're about to exit
931 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000932 # close the listen socket
933 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000934
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000935
Victor Stinner45df8202010-04-28 22:31:17 +0000936@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +0000937class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000938
Christian Heimes180510d2008-03-03 19:15:45 +0000939 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000940
Christian Heimes180510d2008-03-03 19:15:45 +0000941 def setUp(self):
942 """Set up a TCP server to receive log messages, and a SocketHandler
943 pointing to that server's address and port."""
944 BaseTest.setUp(self)
945 self.tcpserver = LogRecordSocketReceiver(port=0)
946 self.port = self.tcpserver.socket.getsockname()[1]
947 self.threads = [
948 threading.Thread(target=self.tcpserver.serve_until_stopped)]
949 for thread in self.threads:
950 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000951
Christian Heimes180510d2008-03-03 19:15:45 +0000952 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
953 self.sock_hdlr.setFormatter(self.root_formatter)
954 self.root_logger.removeHandler(self.root_logger.handlers[0])
955 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000956
Christian Heimes180510d2008-03-03 19:15:45 +0000957 def tearDown(self):
958 """Shutdown the TCP server."""
959 try:
960 self.tcpserver.abort = True
961 del self.tcpserver
962 self.root_logger.removeHandler(self.sock_hdlr)
963 self.sock_hdlr.close()
964 for thread in self.threads:
965 thread.join(2.0)
966 finally:
967 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000968
Christian Heimes180510d2008-03-03 19:15:45 +0000969 def get_output(self):
970 """Get the log output as received by the TCP server."""
971 # Signal the TCP receiver and wait for it to terminate.
972 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
973 self.tcpserver.finished.wait(2.0)
974 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000975
Christian Heimes180510d2008-03-03 19:15:45 +0000976 def test_output(self):
977 # The log message sent to the SocketHandler is properly received.
978 logger = logging.getLogger("tcp")
979 logger.error("spam")
980 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000981 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000982
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000983
Christian Heimes180510d2008-03-03 19:15:45 +0000984class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000985
Christian Heimes180510d2008-03-03 19:15:45 +0000986 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000987
Christian Heimes180510d2008-03-03 19:15:45 +0000988 def setUp(self):
989 """Create a dict to remember potentially destroyed objects."""
990 BaseTest.setUp(self)
991 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000992
Christian Heimes180510d2008-03-03 19:15:45 +0000993 def _watch_for_survival(self, *args):
994 """Watch the given objects for survival, by creating weakrefs to
995 them."""
996 for obj in args:
997 key = id(obj), repr(obj)
998 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000999
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001000 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001001 """Assert that all objects watched for survival have survived."""
1002 # Trigger cycle breaking.
1003 gc.collect()
1004 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001005 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001006 if ref() is None:
1007 dead.append(repr_)
1008 if dead:
1009 self.fail("%d objects should have survived "
1010 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001011
Christian Heimes180510d2008-03-03 19:15:45 +00001012 def test_persistent_loggers(self):
1013 # Logger objects are persistent and retain their configuration, even
1014 # if visible references are destroyed.
1015 self.root_logger.setLevel(logging.INFO)
1016 foo = logging.getLogger("foo")
1017 self._watch_for_survival(foo)
1018 foo.setLevel(logging.DEBUG)
1019 self.root_logger.debug(self.next_message())
1020 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001021 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001022 ('foo', 'DEBUG', '2'),
1023 ])
1024 del foo
1025 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001026 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001027 # foo has retained its settings.
1028 bar = logging.getLogger("foo")
1029 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001030 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001031 ('foo', 'DEBUG', '2'),
1032 ('foo', 'DEBUG', '3'),
1033 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001034
Benjamin Petersonf91df042009-02-13 02:50:59 +00001035
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001036class EncodingTest(BaseTest):
1037 def test_encoding_plain_file(self):
1038 # In Python 2.x, a plain file object is treated as having no encoding.
1039 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001040 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1041 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001042 # the non-ascii data we write to the log.
1043 data = "foo\x80"
1044 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001045 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001046 log.addHandler(handler)
1047 try:
1048 # write non-ascii data to the log.
1049 log.warning(data)
1050 finally:
1051 log.removeHandler(handler)
1052 handler.close()
1053 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001054 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001055 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001056 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001057 finally:
1058 f.close()
1059 finally:
1060 if os.path.isfile(fn):
1061 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001062
Benjamin Petersonf91df042009-02-13 02:50:59 +00001063 def test_encoding_cyrillic_unicode(self):
1064 log = logging.getLogger("test")
1065 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1066 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1067 #Ensure it's written in a Cyrillic encoding
1068 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001069 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001070 stream = io.BytesIO()
1071 writer = writer_class(stream, 'strict')
1072 handler = logging.StreamHandler(writer)
1073 log.addHandler(handler)
1074 try:
1075 log.warning(message)
1076 finally:
1077 log.removeHandler(handler)
1078 handler.close()
1079 # check we wrote exactly those bytes, ignoring trailing \n etc
1080 s = stream.getvalue()
1081 #Compare against what the data should be when encoded in CP-1251
1082 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1083
1084
Georg Brandlf9734072008-12-07 15:30:06 +00001085class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001086
Georg Brandlf9734072008-12-07 15:30:06 +00001087 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001088 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001089 logging.captureWarnings(True)
Brett Cannondf8709d2009-04-01 20:01:47 +00001090 try:
Brett Cannon5b9082a2009-04-05 18:57:32 +00001091 warnings.filterwarnings("always", category=UserWarning)
Brett Cannondf8709d2009-04-01 20:01:47 +00001092 file = io.StringIO()
1093 h = logging.StreamHandler(file)
1094 logger = logging.getLogger("py.warnings")
1095 logger.addHandler(h)
1096 warnings.warn("I'm warning you...")
1097 logger.removeHandler(h)
1098 s = file.getvalue()
1099 h.close()
1100 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001101
Brett Cannondf8709d2009-04-01 20:01:47 +00001102 #See if an explicit file uses the original implementation
1103 file = io.StringIO()
1104 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1105 file, "Dummy line")
1106 s = file.getvalue()
1107 file.close()
1108 self.assertEqual(s,
1109 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1110 finally:
1111 logging.captureWarnings(False)
Georg Brandlf9734072008-12-07 15:30:06 +00001112
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001113
1114def formatFunc(format, datefmt=None):
1115 return logging.Formatter(format, datefmt)
1116
1117def handlerFunc():
1118 return logging.StreamHandler()
1119
1120class CustomHandler(logging.StreamHandler):
1121 pass
1122
1123class ConfigDictTest(BaseTest):
1124
1125 """Reading logging config from a dictionary."""
1126
1127 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1128
1129 # config0 is a standard configuration.
1130 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001131 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001132 'formatters': {
1133 'form1' : {
1134 'format' : '%(levelname)s ++ %(message)s',
1135 },
1136 },
1137 'handlers' : {
1138 'hand1' : {
1139 'class' : 'logging.StreamHandler',
1140 'formatter' : 'form1',
1141 'level' : 'NOTSET',
1142 'stream' : 'ext://sys.stdout',
1143 },
1144 },
1145 'root' : {
1146 'level' : 'WARNING',
1147 'handlers' : ['hand1'],
1148 },
1149 }
1150
1151 # config1 adds a little to the standard configuration.
1152 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001153 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001154 'formatters': {
1155 'form1' : {
1156 'format' : '%(levelname)s ++ %(message)s',
1157 },
1158 },
1159 'handlers' : {
1160 'hand1' : {
1161 'class' : 'logging.StreamHandler',
1162 'formatter' : 'form1',
1163 'level' : 'NOTSET',
1164 'stream' : 'ext://sys.stdout',
1165 },
1166 },
1167 'loggers' : {
1168 'compiler.parser' : {
1169 'level' : 'DEBUG',
1170 'handlers' : ['hand1'],
1171 },
1172 },
1173 'root' : {
1174 'level' : 'WARNING',
1175 },
1176 }
1177
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001178 # config1a moves the handler to the root. Used with config8a
1179 config1a = {
1180 'version': 1,
1181 'formatters': {
1182 'form1' : {
1183 'format' : '%(levelname)s ++ %(message)s',
1184 },
1185 },
1186 'handlers' : {
1187 'hand1' : {
1188 'class' : 'logging.StreamHandler',
1189 'formatter' : 'form1',
1190 'level' : 'NOTSET',
1191 'stream' : 'ext://sys.stdout',
1192 },
1193 },
1194 'loggers' : {
1195 'compiler.parser' : {
1196 'level' : 'DEBUG',
1197 },
1198 },
1199 'root' : {
1200 'level' : 'WARNING',
1201 'handlers' : ['hand1'],
1202 },
1203 }
1204
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001205 # config2 has a subtle configuration error that should be reported
1206 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001207 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001208 'formatters': {
1209 'form1' : {
1210 'format' : '%(levelname)s ++ %(message)s',
1211 },
1212 },
1213 'handlers' : {
1214 'hand1' : {
1215 'class' : 'logging.StreamHandler',
1216 'formatter' : 'form1',
1217 'level' : 'NOTSET',
1218 'stream' : 'ext://sys.stdbout',
1219 },
1220 },
1221 'loggers' : {
1222 'compiler.parser' : {
1223 'level' : 'DEBUG',
1224 'handlers' : ['hand1'],
1225 },
1226 },
1227 'root' : {
1228 'level' : 'WARNING',
1229 },
1230 }
1231
1232 #As config1 but with a misspelt level on a handler
1233 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001234 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001235 'formatters': {
1236 'form1' : {
1237 'format' : '%(levelname)s ++ %(message)s',
1238 },
1239 },
1240 'handlers' : {
1241 'hand1' : {
1242 'class' : 'logging.StreamHandler',
1243 'formatter' : 'form1',
1244 'level' : 'NTOSET',
1245 'stream' : 'ext://sys.stdout',
1246 },
1247 },
1248 'loggers' : {
1249 'compiler.parser' : {
1250 'level' : 'DEBUG',
1251 'handlers' : ['hand1'],
1252 },
1253 },
1254 'root' : {
1255 'level' : 'WARNING',
1256 },
1257 }
1258
1259
1260 #As config1 but with a misspelt level on a logger
1261 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001262 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001263 'formatters': {
1264 'form1' : {
1265 'format' : '%(levelname)s ++ %(message)s',
1266 },
1267 },
1268 'handlers' : {
1269 'hand1' : {
1270 'class' : 'logging.StreamHandler',
1271 'formatter' : 'form1',
1272 'level' : 'NOTSET',
1273 'stream' : 'ext://sys.stdout',
1274 },
1275 },
1276 'loggers' : {
1277 'compiler.parser' : {
1278 'level' : 'DEBUG',
1279 'handlers' : ['hand1'],
1280 },
1281 },
1282 'root' : {
1283 'level' : 'WRANING',
1284 },
1285 }
1286
1287 # config3 has a less subtle configuration error
1288 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001289 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001290 'formatters': {
1291 'form1' : {
1292 'format' : '%(levelname)s ++ %(message)s',
1293 },
1294 },
1295 'handlers' : {
1296 'hand1' : {
1297 'class' : 'logging.StreamHandler',
1298 'formatter' : 'misspelled_name',
1299 'level' : 'NOTSET',
1300 'stream' : 'ext://sys.stdout',
1301 },
1302 },
1303 'loggers' : {
1304 'compiler.parser' : {
1305 'level' : 'DEBUG',
1306 'handlers' : ['hand1'],
1307 },
1308 },
1309 'root' : {
1310 'level' : 'WARNING',
1311 },
1312 }
1313
1314 # config4 specifies a custom formatter class to be loaded
1315 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001316 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001317 'formatters': {
1318 'form1' : {
1319 '()' : __name__ + '.ExceptionFormatter',
1320 'format' : '%(levelname)s:%(name)s:%(message)s',
1321 },
1322 },
1323 'handlers' : {
1324 'hand1' : {
1325 'class' : 'logging.StreamHandler',
1326 'formatter' : 'form1',
1327 'level' : 'NOTSET',
1328 'stream' : 'ext://sys.stdout',
1329 },
1330 },
1331 'root' : {
1332 'level' : 'NOTSET',
1333 'handlers' : ['hand1'],
1334 },
1335 }
1336
1337 # As config4 but using an actual callable rather than a string
1338 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001339 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001340 'formatters': {
1341 'form1' : {
1342 '()' : ExceptionFormatter,
1343 'format' : '%(levelname)s:%(name)s:%(message)s',
1344 },
1345 'form2' : {
1346 '()' : __name__ + '.formatFunc',
1347 'format' : '%(levelname)s:%(name)s:%(message)s',
1348 },
1349 'form3' : {
1350 '()' : formatFunc,
1351 'format' : '%(levelname)s:%(name)s:%(message)s',
1352 },
1353 },
1354 'handlers' : {
1355 'hand1' : {
1356 'class' : 'logging.StreamHandler',
1357 'formatter' : 'form1',
1358 'level' : 'NOTSET',
1359 'stream' : 'ext://sys.stdout',
1360 },
1361 'hand2' : {
1362 '()' : handlerFunc,
1363 },
1364 },
1365 'root' : {
1366 'level' : 'NOTSET',
1367 'handlers' : ['hand1'],
1368 },
1369 }
1370
1371 # config5 specifies a custom handler class to be loaded
1372 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001373 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001374 'formatters': {
1375 'form1' : {
1376 'format' : '%(levelname)s ++ %(message)s',
1377 },
1378 },
1379 'handlers' : {
1380 'hand1' : {
1381 'class' : __name__ + '.CustomHandler',
1382 'formatter' : 'form1',
1383 'level' : 'NOTSET',
1384 'stream' : 'ext://sys.stdout',
1385 },
1386 },
1387 'loggers' : {
1388 'compiler.parser' : {
1389 'level' : 'DEBUG',
1390 'handlers' : ['hand1'],
1391 },
1392 },
1393 'root' : {
1394 'level' : 'WARNING',
1395 },
1396 }
1397
1398 # config6 specifies a custom handler class to be loaded
1399 # but has bad arguments
1400 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001401 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001402 'formatters': {
1403 'form1' : {
1404 'format' : '%(levelname)s ++ %(message)s',
1405 },
1406 },
1407 'handlers' : {
1408 'hand1' : {
1409 'class' : __name__ + '.CustomHandler',
1410 'formatter' : 'form1',
1411 'level' : 'NOTSET',
1412 'stream' : 'ext://sys.stdout',
1413 '9' : 'invalid parameter name',
1414 },
1415 },
1416 'loggers' : {
1417 'compiler.parser' : {
1418 'level' : 'DEBUG',
1419 'handlers' : ['hand1'],
1420 },
1421 },
1422 'root' : {
1423 'level' : 'WARNING',
1424 },
1425 }
1426
1427 #config 7 does not define compiler.parser but defines compiler.lexer
1428 #so compiler.parser should be disabled after applying it
1429 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001430 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001431 'formatters': {
1432 'form1' : {
1433 'format' : '%(levelname)s ++ %(message)s',
1434 },
1435 },
1436 'handlers' : {
1437 'hand1' : {
1438 'class' : 'logging.StreamHandler',
1439 'formatter' : 'form1',
1440 'level' : 'NOTSET',
1441 'stream' : 'ext://sys.stdout',
1442 },
1443 },
1444 'loggers' : {
1445 'compiler.lexer' : {
1446 'level' : 'DEBUG',
1447 'handlers' : ['hand1'],
1448 },
1449 },
1450 'root' : {
1451 'level' : 'WARNING',
1452 },
1453 }
1454
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001455 # config8 defines both compiler and compiler.lexer
1456 # so compiler.parser should not be disabled (since
1457 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001458 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001459 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001460 'disable_existing_loggers' : False,
1461 'formatters': {
1462 'form1' : {
1463 'format' : '%(levelname)s ++ %(message)s',
1464 },
1465 },
1466 'handlers' : {
1467 'hand1' : {
1468 'class' : 'logging.StreamHandler',
1469 'formatter' : 'form1',
1470 'level' : 'NOTSET',
1471 'stream' : 'ext://sys.stdout',
1472 },
1473 },
1474 'loggers' : {
1475 'compiler' : {
1476 'level' : 'DEBUG',
1477 'handlers' : ['hand1'],
1478 },
1479 'compiler.lexer' : {
1480 },
1481 },
1482 'root' : {
1483 'level' : 'WARNING',
1484 },
1485 }
1486
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001487 # config8a disables existing loggers
1488 config8a = {
1489 'version': 1,
1490 'disable_existing_loggers' : True,
1491 'formatters': {
1492 'form1' : {
1493 'format' : '%(levelname)s ++ %(message)s',
1494 },
1495 },
1496 'handlers' : {
1497 'hand1' : {
1498 'class' : 'logging.StreamHandler',
1499 'formatter' : 'form1',
1500 'level' : 'NOTSET',
1501 'stream' : 'ext://sys.stdout',
1502 },
1503 },
1504 'loggers' : {
1505 'compiler' : {
1506 'level' : 'DEBUG',
1507 'handlers' : ['hand1'],
1508 },
1509 'compiler.lexer' : {
1510 },
1511 },
1512 'root' : {
1513 'level' : 'WARNING',
1514 },
1515 }
1516
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001517 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001518 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001519 'formatters': {
1520 'form1' : {
1521 'format' : '%(levelname)s ++ %(message)s',
1522 },
1523 },
1524 'handlers' : {
1525 'hand1' : {
1526 'class' : 'logging.StreamHandler',
1527 'formatter' : 'form1',
1528 'level' : 'WARNING',
1529 'stream' : 'ext://sys.stdout',
1530 },
1531 },
1532 'loggers' : {
1533 'compiler.parser' : {
1534 'level' : 'WARNING',
1535 'handlers' : ['hand1'],
1536 },
1537 },
1538 'root' : {
1539 'level' : 'NOTSET',
1540 },
1541 }
1542
1543 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001544 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001545 'incremental' : True,
1546 'handlers' : {
1547 'hand1' : {
1548 'level' : 'WARNING',
1549 },
1550 },
1551 'loggers' : {
1552 'compiler.parser' : {
1553 'level' : 'INFO',
1554 },
1555 },
1556 }
1557
1558 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001559 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001560 'incremental' : True,
1561 'handlers' : {
1562 'hand1' : {
1563 'level' : 'INFO',
1564 },
1565 },
1566 'loggers' : {
1567 'compiler.parser' : {
1568 'level' : 'INFO',
1569 },
1570 },
1571 }
1572
1573 #As config1 but with a filter added
1574 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001575 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001576 'formatters': {
1577 'form1' : {
1578 'format' : '%(levelname)s ++ %(message)s',
1579 },
1580 },
1581 'filters' : {
1582 'filt1' : {
1583 'name' : 'compiler.parser',
1584 },
1585 },
1586 'handlers' : {
1587 'hand1' : {
1588 'class' : 'logging.StreamHandler',
1589 'formatter' : 'form1',
1590 'level' : 'NOTSET',
1591 'stream' : 'ext://sys.stdout',
1592 'filters' : ['filt1'],
1593 },
1594 },
1595 'loggers' : {
1596 'compiler.parser' : {
1597 'level' : 'DEBUG',
1598 'filters' : ['filt1'],
1599 },
1600 },
1601 'root' : {
1602 'level' : 'WARNING',
1603 'handlers' : ['hand1'],
1604 },
1605 }
1606
1607 #As config1 but using cfg:// references
1608 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001609 'version': 1,
1610 'true_formatters': {
1611 'form1' : {
1612 'format' : '%(levelname)s ++ %(message)s',
1613 },
1614 },
1615 'handler_configs': {
1616 'hand1' : {
1617 'class' : 'logging.StreamHandler',
1618 'formatter' : 'form1',
1619 'level' : 'NOTSET',
1620 'stream' : 'ext://sys.stdout',
1621 },
1622 },
1623 'formatters' : 'cfg://true_formatters',
1624 'handlers' : {
1625 'hand1' : 'cfg://handler_configs[hand1]',
1626 },
1627 'loggers' : {
1628 'compiler.parser' : {
1629 'level' : 'DEBUG',
1630 'handlers' : ['hand1'],
1631 },
1632 },
1633 'root' : {
1634 'level' : 'WARNING',
1635 },
1636 }
1637
1638 #As config11 but missing the version key
1639 config12 = {
1640 'true_formatters': {
1641 'form1' : {
1642 'format' : '%(levelname)s ++ %(message)s',
1643 },
1644 },
1645 'handler_configs': {
1646 'hand1' : {
1647 'class' : 'logging.StreamHandler',
1648 'formatter' : 'form1',
1649 'level' : 'NOTSET',
1650 'stream' : 'ext://sys.stdout',
1651 },
1652 },
1653 'formatters' : 'cfg://true_formatters',
1654 'handlers' : {
1655 'hand1' : 'cfg://handler_configs[hand1]',
1656 },
1657 'loggers' : {
1658 'compiler.parser' : {
1659 'level' : 'DEBUG',
1660 'handlers' : ['hand1'],
1661 },
1662 },
1663 'root' : {
1664 'level' : 'WARNING',
1665 },
1666 }
1667
1668 #As config11 but using an unsupported version
1669 config13 = {
1670 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001671 'true_formatters': {
1672 'form1' : {
1673 'format' : '%(levelname)s ++ %(message)s',
1674 },
1675 },
1676 'handler_configs': {
1677 'hand1' : {
1678 'class' : 'logging.StreamHandler',
1679 'formatter' : 'form1',
1680 'level' : 'NOTSET',
1681 'stream' : 'ext://sys.stdout',
1682 },
1683 },
1684 'formatters' : 'cfg://true_formatters',
1685 'handlers' : {
1686 'hand1' : 'cfg://handler_configs[hand1]',
1687 },
1688 'loggers' : {
1689 'compiler.parser' : {
1690 'level' : 'DEBUG',
1691 'handlers' : ['hand1'],
1692 },
1693 },
1694 'root' : {
1695 'level' : 'WARNING',
1696 },
1697 }
1698
1699 def apply_config(self, conf):
1700 logging.config.dictConfig(conf)
1701
1702 def test_config0_ok(self):
1703 # A simple config which overrides the default settings.
1704 with captured_stdout() as output:
1705 self.apply_config(self.config0)
1706 logger = logging.getLogger()
1707 # Won't output anything
1708 logger.info(self.next_message())
1709 # Outputs a message
1710 logger.error(self.next_message())
1711 self.assert_log_lines([
1712 ('ERROR', '2'),
1713 ], stream=output)
1714 # Original logger output is empty.
1715 self.assert_log_lines([])
1716
1717 def test_config1_ok(self, config=config1):
1718 # A config defining a sub-parser as well.
1719 with captured_stdout() as output:
1720 self.apply_config(config)
1721 logger = logging.getLogger("compiler.parser")
1722 # Both will output a message
1723 logger.info(self.next_message())
1724 logger.error(self.next_message())
1725 self.assert_log_lines([
1726 ('INFO', '1'),
1727 ('ERROR', '2'),
1728 ], stream=output)
1729 # Original logger output is empty.
1730 self.assert_log_lines([])
1731
1732 def test_config2_failure(self):
1733 # A simple config which overrides the default settings.
1734 self.assertRaises(Exception, self.apply_config, self.config2)
1735
1736 def test_config2a_failure(self):
1737 # A simple config which overrides the default settings.
1738 self.assertRaises(Exception, self.apply_config, self.config2a)
1739
1740 def test_config2b_failure(self):
1741 # A simple config which overrides the default settings.
1742 self.assertRaises(Exception, self.apply_config, self.config2b)
1743
1744 def test_config3_failure(self):
1745 # A simple config which overrides the default settings.
1746 self.assertRaises(Exception, self.apply_config, self.config3)
1747
1748 def test_config4_ok(self):
1749 # A config specifying a custom formatter class.
1750 with captured_stdout() as output:
1751 self.apply_config(self.config4)
1752 #logger = logging.getLogger()
1753 try:
1754 raise RuntimeError()
1755 except RuntimeError:
1756 logging.exception("just testing")
1757 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001758 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001759 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1760 # Original logger output is empty
1761 self.assert_log_lines([])
1762
1763 def test_config4a_ok(self):
1764 # A config specifying a custom formatter class.
1765 with captured_stdout() as output:
1766 self.apply_config(self.config4a)
1767 #logger = logging.getLogger()
1768 try:
1769 raise RuntimeError()
1770 except RuntimeError:
1771 logging.exception("just testing")
1772 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001773 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001774 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1775 # Original logger output is empty
1776 self.assert_log_lines([])
1777
1778 def test_config5_ok(self):
1779 self.test_config1_ok(config=self.config5)
1780
1781 def test_config6_failure(self):
1782 self.assertRaises(Exception, self.apply_config, self.config6)
1783
1784 def test_config7_ok(self):
1785 with captured_stdout() as output:
1786 self.apply_config(self.config1)
1787 logger = logging.getLogger("compiler.parser")
1788 # Both will output a message
1789 logger.info(self.next_message())
1790 logger.error(self.next_message())
1791 self.assert_log_lines([
1792 ('INFO', '1'),
1793 ('ERROR', '2'),
1794 ], stream=output)
1795 # Original logger output is empty.
1796 self.assert_log_lines([])
1797 with captured_stdout() as output:
1798 self.apply_config(self.config7)
1799 logger = logging.getLogger("compiler.parser")
1800 self.assertTrue(logger.disabled)
1801 logger = logging.getLogger("compiler.lexer")
1802 # Both will output a message
1803 logger.info(self.next_message())
1804 logger.error(self.next_message())
1805 self.assert_log_lines([
1806 ('INFO', '3'),
1807 ('ERROR', '4'),
1808 ], stream=output)
1809 # Original logger output is empty.
1810 self.assert_log_lines([])
1811
1812 #Same as test_config_7_ok but don't disable old loggers.
1813 def test_config_8_ok(self):
1814 with captured_stdout() as output:
1815 self.apply_config(self.config1)
1816 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001817 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001818 logger.info(self.next_message())
1819 logger.error(self.next_message())
1820 self.assert_log_lines([
1821 ('INFO', '1'),
1822 ('ERROR', '2'),
1823 ], stream=output)
1824 # Original logger output is empty.
1825 self.assert_log_lines([])
1826 with captured_stdout() as output:
1827 self.apply_config(self.config8)
1828 logger = logging.getLogger("compiler.parser")
1829 self.assertFalse(logger.disabled)
1830 # Both will output a message
1831 logger.info(self.next_message())
1832 logger.error(self.next_message())
1833 logger = logging.getLogger("compiler.lexer")
1834 # Both will output a message
1835 logger.info(self.next_message())
1836 logger.error(self.next_message())
1837 self.assert_log_lines([
1838 ('INFO', '3'),
1839 ('ERROR', '4'),
1840 ('INFO', '5'),
1841 ('ERROR', '6'),
1842 ], stream=output)
1843 # Original logger output is empty.
1844 self.assert_log_lines([])
1845
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001846 def test_config_8a_ok(self):
1847 with captured_stdout() as output:
1848 self.apply_config(self.config1a)
1849 logger = logging.getLogger("compiler.parser")
1850 # See issue #11424. compiler-hyphenated sorts
1851 # between compiler and compiler.xyz and this
1852 # was preventing compiler.xyz from being included
1853 # in the child loggers of compiler because of an
1854 # overzealous loop termination condition.
1855 hyphenated = logging.getLogger('compiler-hyphenated')
1856 # All will output a message
1857 logger.info(self.next_message())
1858 logger.error(self.next_message())
1859 hyphenated.critical(self.next_message())
1860 self.assert_log_lines([
1861 ('INFO', '1'),
1862 ('ERROR', '2'),
1863 ('CRITICAL', '3'),
1864 ], stream=output)
1865 # Original logger output is empty.
1866 self.assert_log_lines([])
1867 with captured_stdout() as output:
1868 self.apply_config(self.config8a)
1869 logger = logging.getLogger("compiler.parser")
1870 self.assertFalse(logger.disabled)
1871 # Both will output a message
1872 logger.info(self.next_message())
1873 logger.error(self.next_message())
1874 logger = logging.getLogger("compiler.lexer")
1875 # Both will output a message
1876 logger.info(self.next_message())
1877 logger.error(self.next_message())
1878 # Will not appear
1879 hyphenated.critical(self.next_message())
1880 self.assert_log_lines([
1881 ('INFO', '4'),
1882 ('ERROR', '5'),
1883 ('INFO', '6'),
1884 ('ERROR', '7'),
1885 ], stream=output)
1886 # Original logger output is empty.
1887 self.assert_log_lines([])
1888
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001889 def test_config_9_ok(self):
1890 with captured_stdout() as output:
1891 self.apply_config(self.config9)
1892 logger = logging.getLogger("compiler.parser")
1893 #Nothing will be output since both handler and logger are set to WARNING
1894 logger.info(self.next_message())
1895 self.assert_log_lines([], stream=output)
1896 self.apply_config(self.config9a)
1897 #Nothing will be output since both handler is still set to WARNING
1898 logger.info(self.next_message())
1899 self.assert_log_lines([], stream=output)
1900 self.apply_config(self.config9b)
1901 #Message should now be output
1902 logger.info(self.next_message())
1903 self.assert_log_lines([
1904 ('INFO', '3'),
1905 ], stream=output)
1906
1907 def test_config_10_ok(self):
1908 with captured_stdout() as output:
1909 self.apply_config(self.config10)
1910 logger = logging.getLogger("compiler.parser")
1911 logger.warning(self.next_message())
1912 logger = logging.getLogger('compiler')
1913 #Not output, because filtered
1914 logger.warning(self.next_message())
1915 logger = logging.getLogger('compiler.lexer')
1916 #Not output, because filtered
1917 logger.warning(self.next_message())
1918 logger = logging.getLogger("compiler.parser.codegen")
1919 #Output, as not filtered
1920 logger.error(self.next_message())
1921 self.assert_log_lines([
1922 ('WARNING', '1'),
1923 ('ERROR', '4'),
1924 ], stream=output)
1925
1926 def test_config11_ok(self):
1927 self.test_config1_ok(self.config11)
1928
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001929 def test_config12_failure(self):
1930 self.assertRaises(Exception, self.apply_config, self.config12)
1931
1932 def test_config13_failure(self):
1933 self.assertRaises(Exception, self.apply_config, self.config13)
1934
Victor Stinner45df8202010-04-28 22:31:17 +00001935 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001936 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001937 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00001938 # Ask for a randomly assigned port (by using port 0)
1939 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001940 t.start()
1941 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00001942 # Now get the port allocated
1943 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001944 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001945 try:
1946 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1947 sock.settimeout(2.0)
1948 sock.connect(('localhost', port))
1949
1950 slen = struct.pack('>L', len(text))
1951 s = slen + text
1952 sentsofar = 0
1953 left = len(s)
1954 while left > 0:
1955 sent = sock.send(s[sentsofar:])
1956 sentsofar += sent
1957 left -= sent
1958 sock.close()
1959 finally:
1960 t.ready.wait(2.0)
1961 logging.config.stopListening()
1962 t.join(2.0)
1963
1964 def test_listen_config_10_ok(self):
1965 with captured_stdout() as output:
1966 self.setup_via_listener(json.dumps(self.config10))
1967 logger = logging.getLogger("compiler.parser")
1968 logger.warning(self.next_message())
1969 logger = logging.getLogger('compiler')
1970 #Not output, because filtered
1971 logger.warning(self.next_message())
1972 logger = logging.getLogger('compiler.lexer')
1973 #Not output, because filtered
1974 logger.warning(self.next_message())
1975 logger = logging.getLogger("compiler.parser.codegen")
1976 #Output, as not filtered
1977 logger.error(self.next_message())
1978 self.assert_log_lines([
1979 ('WARNING', '1'),
1980 ('ERROR', '4'),
1981 ], stream=output)
1982
1983 def test_listen_config_1_ok(self):
1984 with captured_stdout() as output:
1985 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
1986 logger = logging.getLogger("compiler.parser")
1987 # Both will output a message
1988 logger.info(self.next_message())
1989 logger.error(self.next_message())
1990 self.assert_log_lines([
1991 ('INFO', '1'),
1992 ('ERROR', '2'),
1993 ], stream=output)
1994 # Original logger output is empty.
1995 self.assert_log_lines([])
1996
1997
1998class ManagerTest(BaseTest):
1999 def test_manager_loggerclass(self):
2000 logged = []
2001
2002 class MyLogger(logging.Logger):
2003 def _log(self, level, msg, args, exc_info=None, extra=None):
2004 logged.append(msg)
2005
2006 man = logging.Manager(None)
2007 self.assertRaises(TypeError, man.setLoggerClass, int)
2008 man.setLoggerClass(MyLogger)
2009 logger = man.getLogger('test')
2010 logger.warning('should appear in logged')
2011 logging.warning('should not appear in logged')
2012
2013 self.assertEqual(logged, ['should appear in logged'])
2014
2015
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002016class ChildLoggerTest(BaseTest):
2017 def test_child_loggers(self):
2018 r = logging.getLogger()
2019 l1 = logging.getLogger('abc')
2020 l2 = logging.getLogger('def.ghi')
2021 c1 = r.getChild('xyz')
2022 c2 = r.getChild('uvw.xyz')
2023 self.assertTrue(c1 is logging.getLogger('xyz'))
2024 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2025 c1 = l1.getChild('def')
2026 c2 = c1.getChild('ghi')
2027 c3 = l1.getChild('def.ghi')
2028 self.assertTrue(c1 is logging.getLogger('abc.def'))
2029 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2030 self.assertTrue(c2 is c3)
2031
2032
Vinay Sajip6fac8172010-10-19 20:44:14 +00002033class DerivedLogRecord(logging.LogRecord):
2034 pass
2035
Vinay Sajip61561522010-12-03 11:50:38 +00002036class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002037
2038 def setUp(self):
2039 class CheckingFilter(logging.Filter):
2040 def __init__(self, cls):
2041 self.cls = cls
2042
2043 def filter(self, record):
2044 t = type(record)
2045 if t is not self.cls:
2046 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2047 self.cls)
2048 raise TypeError(msg)
2049 return True
2050
2051 BaseTest.setUp(self)
2052 self.filter = CheckingFilter(DerivedLogRecord)
2053 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002054 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002055
2056 def tearDown(self):
2057 self.root_logger.removeFilter(self.filter)
2058 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002059 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002060
2061 def test_logrecord_class(self):
2062 self.assertRaises(TypeError, self.root_logger.warning,
2063 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002064 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002065 self.root_logger.error(self.next_message())
2066 self.assert_log_lines([
2067 ('root', 'ERROR', '2'),
2068 ])
2069
2070
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002071class QueueHandlerTest(BaseTest):
2072 # Do not bother with a logger name group.
2073 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2074
2075 def setUp(self):
2076 BaseTest.setUp(self)
2077 self.queue = queue.Queue(-1)
2078 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2079 self.que_logger = logging.getLogger('que')
2080 self.que_logger.propagate = False
2081 self.que_logger.setLevel(logging.WARNING)
2082 self.que_logger.addHandler(self.que_hdlr)
2083
2084 def tearDown(self):
2085 self.que_hdlr.close()
2086 BaseTest.tearDown(self)
2087
2088 def test_queue_handler(self):
2089 self.que_logger.debug(self.next_message())
2090 self.assertRaises(queue.Empty, self.queue.get_nowait)
2091 self.que_logger.info(self.next_message())
2092 self.assertRaises(queue.Empty, self.queue.get_nowait)
2093 msg = self.next_message()
2094 self.que_logger.warning(msg)
2095 data = self.queue.get_nowait()
2096 self.assertTrue(isinstance(data, logging.LogRecord))
2097 self.assertEqual(data.name, self.que_logger.name)
2098 self.assertEqual((data.msg, data.args), (msg, None))
2099
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002100 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2101 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002102 def test_queue_listener(self):
2103 handler = TestHandler(Matcher())
2104 listener = logging.handlers.QueueListener(self.queue, handler)
2105 listener.start()
2106 try:
2107 self.que_logger.warning(self.next_message())
2108 self.que_logger.error(self.next_message())
2109 self.que_logger.critical(self.next_message())
2110 finally:
2111 listener.stop()
2112 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2113 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2114 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2115
2116
Vinay Sajipa39c5712010-10-25 13:57:39 +00002117class FormatterTest(unittest.TestCase):
2118 def setUp(self):
2119 self.common = {
2120 'name': 'formatter.test',
2121 'level': logging.DEBUG,
2122 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2123 'lineno': 42,
2124 'exc_info': None,
2125 'func': None,
2126 'msg': 'Message with %d %s',
2127 'args': (2, 'placeholders'),
2128 }
2129 self.variants = {
2130 }
2131
2132 def get_record(self, name=None):
2133 result = dict(self.common)
2134 if name is not None:
2135 result.update(self.variants[name])
2136 return logging.makeLogRecord(result)
2137
2138 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002139 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002140 r = self.get_record()
2141 f = logging.Formatter('${%(message)s}')
2142 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2143 f = logging.Formatter('%(random)s')
2144 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002145 self.assertFalse(f.usesTime())
2146 f = logging.Formatter('%(asctime)s')
2147 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002148 f = logging.Formatter('%(asctime)-15s')
2149 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002150 f = logging.Formatter('asctime')
2151 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002152
2153 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002154 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002155 r = self.get_record()
2156 f = logging.Formatter('$%{message}%$', style='{')
2157 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2158 f = logging.Formatter('{random}', style='{')
2159 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002160 self.assertFalse(f.usesTime())
2161 f = logging.Formatter('{asctime}', style='{')
2162 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002163 f = logging.Formatter('{asctime!s:15}', style='{')
2164 self.assertTrue(f.usesTime())
2165 f = logging.Formatter('{asctime:15}', style='{')
2166 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002167 f = logging.Formatter('asctime', style='{')
2168 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002169
2170 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002171 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002172 r = self.get_record()
2173 f = logging.Formatter('$message', style='$')
2174 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2175 f = logging.Formatter('$$%${message}%$$', style='$')
2176 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2177 f = logging.Formatter('${random}', style='$')
2178 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002179 self.assertFalse(f.usesTime())
2180 f = logging.Formatter('${asctime}', style='$')
2181 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002182 f = logging.Formatter('${asctime', style='$')
2183 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002184 f = logging.Formatter('$asctime', style='$')
2185 self.assertTrue(f.usesTime())
2186 f = logging.Formatter('asctime', style='$')
2187 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002188
Vinay Sajip5a27d402010-12-10 11:42:57 +00002189class LastResortTest(BaseTest):
2190 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002191 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002192 root = self.root_logger
2193 root.removeHandler(self.root_hdlr)
2194 old_stderr = sys.stderr
2195 old_lastresort = logging.lastResort
2196 old_raise_exceptions = logging.raiseExceptions
2197 try:
2198 sys.stderr = sio = io.StringIO()
2199 root.warning('This is your final chance!')
2200 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2201 #No handlers and no last resort, so 'No handlers' message
2202 logging.lastResort = None
2203 sys.stderr = sio = io.StringIO()
2204 root.warning('This is your final chance!')
2205 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2206 # 'No handlers' message only printed once
2207 sys.stderr = sio = io.StringIO()
2208 root.warning('This is your final chance!')
2209 self.assertEqual(sio.getvalue(), '')
2210 root.manager.emittedNoHandlerWarning = False
2211 #If raiseExceptions is False, no message is printed
2212 logging.raiseExceptions = False
2213 sys.stderr = sio = io.StringIO()
2214 root.warning('This is your final chance!')
2215 self.assertEqual(sio.getvalue(), '')
2216 finally:
2217 sys.stderr = old_stderr
2218 root.addHandler(self.root_hdlr)
2219 logging.lastResort = old_lastresort
2220 logging.raiseExceptions = old_raise_exceptions
2221
2222
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002223class BaseFileTest(BaseTest):
2224 "Base class for handler tests that write log files"
2225
2226 def setUp(self):
2227 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002228 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2229 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002230 self.rmfiles = []
2231
2232 def tearDown(self):
2233 for fn in self.rmfiles:
2234 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002235 if os.path.exists(self.fn):
2236 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002237 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002238
2239 def assertLogFile(self, filename):
2240 "Assert a log file is there and register it for deletion"
2241 self.assertTrue(os.path.exists(filename),
2242 msg="Log file %r does not exist")
2243 self.rmfiles.append(filename)
2244
2245
2246class RotatingFileHandlerTest(BaseFileTest):
2247 def next_rec(self):
2248 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2249 self.next_message(), None, None, None)
2250
2251 def test_should_not_rollover(self):
2252 # If maxbytes is zero rollover never occurs
2253 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2254 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002255 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002256
2257 def test_should_rollover(self):
2258 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2259 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002260 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002261
2262 def test_file_created(self):
2263 # checks that the file is created and assumes it was created
2264 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002265 rh = logging.handlers.RotatingFileHandler(self.fn)
2266 rh.emit(self.next_rec())
2267 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002268 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002269
2270 def test_rollover_filenames(self):
2271 rh = logging.handlers.RotatingFileHandler(
2272 self.fn, backupCount=2, maxBytes=1)
2273 rh.emit(self.next_rec())
2274 self.assertLogFile(self.fn)
2275 rh.emit(self.next_rec())
2276 self.assertLogFile(self.fn + ".1")
2277 rh.emit(self.next_rec())
2278 self.assertLogFile(self.fn + ".2")
2279 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002280 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002281
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002282class TimedRotatingFileHandlerTest(BaseFileTest):
2283 # test methods added below
2284 pass
2285
2286def secs(**kw):
2287 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2288
2289for when, exp in (('S', 1),
2290 ('M', 60),
2291 ('H', 60 * 60),
2292 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002293 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002294 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002295 ('W0', secs(days=4, hours=24)),
2296 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002297 def test_compute_rollover(self, when=when, exp=exp):
2298 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002299 self.fn, when=when, interval=1, backupCount=0, utc=True)
2300 currentTime = 0.0
2301 actual = rh.computeRollover(currentTime)
2302 if exp != actual:
2303 # Failures occur on some systems for MIDNIGHT and W0.
2304 # Print detailed calculation for MIDNIGHT so we can try to see
2305 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00002306 if when == 'MIDNIGHT':
2307 try:
2308 if rh.utc:
2309 t = time.gmtime(currentTime)
2310 else:
2311 t = time.localtime(currentTime)
2312 currentHour = t[3]
2313 currentMinute = t[4]
2314 currentSecond = t[5]
2315 # r is the number of seconds left between now and midnight
2316 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
2317 currentMinute) * 60 +
2318 currentSecond)
2319 result = currentTime + r
2320 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
2321 print('currentHour: %s' % currentHour, file=sys.stderr)
2322 print('currentMinute: %s' % currentMinute, file=sys.stderr)
2323 print('currentSecond: %s' % currentSecond, file=sys.stderr)
2324 print('r: %s' % r, file=sys.stderr)
2325 print('result: %s' % result, file=sys.stderr)
2326 except Exception:
2327 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
2328 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00002329 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002330 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
2331
Vinay Sajip66b8b082012-04-24 23:25:30 +01002332class HandlerTest(BaseTest):
2333
2334 @unittest.skipUnless(threading, 'Threading required for this test.')
2335 def test_race(self):
2336 # Issue #14632 refers.
2337 def remove_loop(fname, tries):
2338 for _ in range(tries):
2339 try:
2340 os.unlink(fname)
2341 except OSError:
2342 pass
2343 time.sleep(0.004 * random.randint(0, 4))
2344
2345 def cleanup(remover, fn, handler):
2346 handler.close()
2347 remover.join()
2348 if os.path.exists(fn):
2349 os.unlink(fn)
2350
2351 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
2352 os.close(fd)
2353 del_count = 1000
2354 log_count = 1000
2355 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
2356 remover.daemon = True
2357 remover.start()
2358 for delay in (False, True):
2359 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
2360 self.addCleanup(cleanup, remover, fn, h)
2361 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
2362 h.setFormatter(f)
2363 for _ in range(log_count):
2364 time.sleep(0.005)
2365 r = logging.makeLogRecord({'msg': 'testing' })
2366 h.handle(r)
2367
2368
Christian Heimes180510d2008-03-03 19:15:45 +00002369# Set the locale to the platform-dependent default. I have no idea
2370# why the test does this, but in any case we save the current locale
2371# first and restore it at the end.
2372@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00002373def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00002374 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002375 CustomLevelsAndFiltersTest, MemoryHandlerTest,
2376 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002377 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajipa39c5712010-10-25 13:57:39 +00002378 FormatterTest,
Vinay Sajip61561522010-12-03 11:50:38 +00002379 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipbc85d842010-09-17 23:35:29 +00002380 RotatingFileHandlerTest,
Vinay Sajip5a27d402010-12-10 11:42:57 +00002381 LastResortTest,
Vinay Sajip66b8b082012-04-24 23:25:30 +01002382 TimedRotatingFileHandlerTest, HandlerTest,
Vinay Sajipbc85d842010-09-17 23:35:29 +00002383 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00002384
Christian Heimes180510d2008-03-03 19:15:45 +00002385if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002386 test_main()