blob: 017e9f38dc5ba73ab85c036d8666d85c33404943 [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajiped0473c2011-02-26 15:35:38 +00003# Copyright 2001-2011 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00004#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19"""Test harness for the logging module. Run all tests.
20
Vinay Sajiped0473c2011-02-26 15:35:38 +000021Copyright (C) 2001-2011 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Christian Heimes180510d2008-03-03 19:15:45 +000024import logging
25import logging.handlers
26import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000027
Benjamin Petersonf91df042009-02-13 02:50:59 +000028import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Christian Heimes180510d2008-03-03 19:15:45 +000030import pickle
31import io
32import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000033import json
Christian Heimes180510d2008-03-03 19:15:45 +000034import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000035import queue
Christian Heimes180510d2008-03-03 19:15:45 +000036import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000037import select
Christian Heimes180510d2008-03-03 19:15:45 +000038import socket
Alexandre Vassalottice261952008-05-12 02:31:37 +000039from socketserver import ThreadingTCPServer, StreamRequestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000040import struct
41import sys
42import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010043from test.support import captured_stdout, run_with_locale, run_unittest, patch
Vinay Sajipe723e962011-04-15 22:27:17 +010044from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000045import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010046import time
Christian Heimes180510d2008-03-03 19:15:45 +000047import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000048import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000049import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000050try:
51 import threading
52except ImportError:
53 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000054
55
Christian Heimes180510d2008-03-03 19:15:45 +000056class BaseTest(unittest.TestCase):
57
58 """Base class for logging tests."""
59
60 log_format = "%(name)s -> %(levelname)s: %(message)s"
61 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
62 message_num = 0
63
64 def setUp(self):
65 """Setup the default logging stream to an internal StringIO instance,
66 so that we can examine log output as we want."""
67 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000068 logging._acquireLock()
69 try:
Christian Heimes180510d2008-03-03 19:15:45 +000070 self.saved_handlers = logging._handlers.copy()
71 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000072 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000073 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000074 self.logger_states = logger_states = {}
75 for name in saved_loggers:
76 logger_states[name] = getattr(saved_loggers[name],
77 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000078 finally:
79 logging._releaseLock()
80
Benjamin Peterson22005fc2010-04-11 16:25:06 +000081 # Set two unused loggers: one non-ASCII and one Unicode.
82 # This is to test correct operation when sorting existing
83 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000084 self.logger1 = logging.getLogger("\xab\xd7\xbb")
85 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000086
Christian Heimes180510d2008-03-03 19:15:45 +000087 self.root_logger = logging.getLogger("")
88 self.original_logging_level = self.root_logger.getEffectiveLevel()
89
90 self.stream = io.StringIO()
91 self.root_logger.setLevel(logging.DEBUG)
92 self.root_hdlr = logging.StreamHandler(self.stream)
93 self.root_formatter = logging.Formatter(self.log_format)
94 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +000095 if self.logger1.hasHandlers():
96 hlist = self.logger1.handlers + self.root_logger.handlers
97 raise AssertionError('Unexpected handlers: %s' % hlist)
98 if self.logger2.hasHandlers():
99 hlist = self.logger2.handlers + self.root_logger.handlers
100 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000101 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000102 self.assertTrue(self.logger1.hasHandlers())
103 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000104
105 def tearDown(self):
106 """Remove our logging stream, and restore the original logging
107 level."""
108 self.stream.close()
109 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000110 while self.root_logger.handlers:
111 h = self.root_logger.handlers[0]
112 self.root_logger.removeHandler(h)
113 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000114 self.root_logger.setLevel(self.original_logging_level)
115 logging._acquireLock()
116 try:
117 logging._levelNames.clear()
118 logging._levelNames.update(self.saved_level_names)
119 logging._handlers.clear()
120 logging._handlers.update(self.saved_handlers)
121 logging._handlerList[:] = self.saved_handler_list
122 loggerDict = logging.getLogger().manager.loggerDict
123 loggerDict.clear()
124 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000125 logger_states = self.logger_states
126 for name in self.logger_states:
127 if logger_states[name] is not None:
128 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000129 finally:
130 logging._releaseLock()
131
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000132 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000133 """Match the collected log lines against the regular expression
134 self.expected_log_pat, and compare the extracted group values to
135 the expected_values list of tuples."""
136 stream = stream or self.stream
137 pat = re.compile(self.expected_log_pat)
138 try:
139 stream.reset()
140 actual_lines = stream.readlines()
141 except AttributeError:
142 # StringIO.StringIO lacks a reset() method.
143 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000144 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000145 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000146 for actual, expected in zip(actual_lines, expected_values):
147 match = pat.search(actual)
148 if not match:
149 self.fail("Log line does not match expected pattern:\n" +
150 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000151 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000152 s = stream.read()
153 if s:
154 self.fail("Remaining output at end of log stream:\n" + s)
155
156 def next_message(self):
157 """Generate a message consisting solely of an auto-incrementing
158 integer."""
159 self.message_num += 1
160 return "%d" % self.message_num
161
162
163class BuiltinLevelsTest(BaseTest):
164 """Test builtin levels and their inheritance."""
165
166 def test_flat(self):
167 #Logging levels in a flat logger namespace.
168 m = self.next_message
169
170 ERR = logging.getLogger("ERR")
171 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000172 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000173 INF.setLevel(logging.INFO)
174 DEB = logging.getLogger("DEB")
175 DEB.setLevel(logging.DEBUG)
176
177 # These should log.
178 ERR.log(logging.CRITICAL, m())
179 ERR.error(m())
180
181 INF.log(logging.CRITICAL, m())
182 INF.error(m())
183 INF.warn(m())
184 INF.info(m())
185
186 DEB.log(logging.CRITICAL, m())
187 DEB.error(m())
188 DEB.warn (m())
189 DEB.info (m())
190 DEB.debug(m())
191
192 # These should not log.
193 ERR.warn(m())
194 ERR.info(m())
195 ERR.debug(m())
196
197 INF.debug(m())
198
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000199 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000200 ('ERR', 'CRITICAL', '1'),
201 ('ERR', 'ERROR', '2'),
202 ('INF', 'CRITICAL', '3'),
203 ('INF', 'ERROR', '4'),
204 ('INF', 'WARNING', '5'),
205 ('INF', 'INFO', '6'),
206 ('DEB', 'CRITICAL', '7'),
207 ('DEB', 'ERROR', '8'),
208 ('DEB', 'WARNING', '9'),
209 ('DEB', 'INFO', '10'),
210 ('DEB', 'DEBUG', '11'),
211 ])
212
213 def test_nested_explicit(self):
214 # Logging levels in a nested namespace, all explicitly set.
215 m = self.next_message
216
217 INF = logging.getLogger("INF")
218 INF.setLevel(logging.INFO)
219 INF_ERR = logging.getLogger("INF.ERR")
220 INF_ERR.setLevel(logging.ERROR)
221
222 # These should log.
223 INF_ERR.log(logging.CRITICAL, m())
224 INF_ERR.error(m())
225
226 # These should not log.
227 INF_ERR.warn(m())
228 INF_ERR.info(m())
229 INF_ERR.debug(m())
230
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000231 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000232 ('INF.ERR', 'CRITICAL', '1'),
233 ('INF.ERR', 'ERROR', '2'),
234 ])
235
236 def test_nested_inherited(self):
237 #Logging levels in a nested namespace, inherited from parent loggers.
238 m = self.next_message
239
240 INF = logging.getLogger("INF")
241 INF.setLevel(logging.INFO)
242 INF_ERR = logging.getLogger("INF.ERR")
243 INF_ERR.setLevel(logging.ERROR)
244 INF_UNDEF = logging.getLogger("INF.UNDEF")
245 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
246 UNDEF = logging.getLogger("UNDEF")
247
248 # These should log.
249 INF_UNDEF.log(logging.CRITICAL, m())
250 INF_UNDEF.error(m())
251 INF_UNDEF.warn(m())
252 INF_UNDEF.info(m())
253 INF_ERR_UNDEF.log(logging.CRITICAL, m())
254 INF_ERR_UNDEF.error(m())
255
256 # These should not log.
257 INF_UNDEF.debug(m())
258 INF_ERR_UNDEF.warn(m())
259 INF_ERR_UNDEF.info(m())
260 INF_ERR_UNDEF.debug(m())
261
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000262 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000263 ('INF.UNDEF', 'CRITICAL', '1'),
264 ('INF.UNDEF', 'ERROR', '2'),
265 ('INF.UNDEF', 'WARNING', '3'),
266 ('INF.UNDEF', 'INFO', '4'),
267 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
268 ('INF.ERR.UNDEF', 'ERROR', '6'),
269 ])
270
271 def test_nested_with_virtual_parent(self):
272 # Logging levels when some parent does not exist yet.
273 m = self.next_message
274
275 INF = logging.getLogger("INF")
276 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
277 CHILD = logging.getLogger("INF.BADPARENT")
278 INF.setLevel(logging.INFO)
279
280 # These should log.
281 GRANDCHILD.log(logging.FATAL, m())
282 GRANDCHILD.info(m())
283 CHILD.log(logging.FATAL, m())
284 CHILD.info(m())
285
286 # These should not log.
287 GRANDCHILD.debug(m())
288 CHILD.debug(m())
289
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000290 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000291 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
292 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
293 ('INF.BADPARENT', 'CRITICAL', '3'),
294 ('INF.BADPARENT', 'INFO', '4'),
295 ])
296
297
298class BasicFilterTest(BaseTest):
299
300 """Test the bundled Filter class."""
301
302 def test_filter(self):
303 # Only messages satisfying the specified criteria pass through the
304 # filter.
305 filter_ = logging.Filter("spam.eggs")
306 handler = self.root_logger.handlers[0]
307 try:
308 handler.addFilter(filter_)
309 spam = logging.getLogger("spam")
310 spam_eggs = logging.getLogger("spam.eggs")
311 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
312 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
313
314 spam.info(self.next_message())
315 spam_eggs.info(self.next_message()) # Good.
316 spam_eggs_fish.info(self.next_message()) # Good.
317 spam_bakedbeans.info(self.next_message())
318
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000319 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000320 ('spam.eggs', 'INFO', '2'),
321 ('spam.eggs.fish', 'INFO', '3'),
322 ])
323 finally:
324 handler.removeFilter(filter_)
325
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000326 def test_callable_filter(self):
327 # Only messages satisfying the specified criteria pass through the
328 # filter.
329
330 def filterfunc(record):
331 parts = record.name.split('.')
332 prefix = '.'.join(parts[:2])
333 return prefix == 'spam.eggs'
334
335 handler = self.root_logger.handlers[0]
336 try:
337 handler.addFilter(filterfunc)
338 spam = logging.getLogger("spam")
339 spam_eggs = logging.getLogger("spam.eggs")
340 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
341 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
342
343 spam.info(self.next_message())
344 spam_eggs.info(self.next_message()) # Good.
345 spam_eggs_fish.info(self.next_message()) # Good.
346 spam_bakedbeans.info(self.next_message())
347
348 self.assert_log_lines([
349 ('spam.eggs', 'INFO', '2'),
350 ('spam.eggs.fish', 'INFO', '3'),
351 ])
352 finally:
353 handler.removeFilter(filterfunc)
354
Vinay Sajip985ef872011-04-26 19:34:04 +0100355 def test_empty_filter(self):
356 f = logging.Filter()
357 r = logging.makeLogRecord({'name': 'spam.eggs'})
358 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000359
360#
361# First, we define our levels. There can be as many as you want - the only
362# limitations are that they should be integers, the lowest should be > 0 and
363# larger values mean less information being logged. If you need specific
364# level values which do not fit into these limitations, you can use a
365# mapping dictionary to convert between your application levels and the
366# logging system.
367#
368SILENT = 120
369TACITURN = 119
370TERSE = 118
371EFFUSIVE = 117
372SOCIABLE = 116
373VERBOSE = 115
374TALKATIVE = 114
375GARRULOUS = 113
376CHATTERBOX = 112
377BORING = 111
378
379LEVEL_RANGE = range(BORING, SILENT + 1)
380
381#
382# Next, we define names for our levels. You don't need to do this - in which
383# case the system will use "Level n" to denote the text for the level.
384#
385my_logging_levels = {
386 SILENT : 'Silent',
387 TACITURN : 'Taciturn',
388 TERSE : 'Terse',
389 EFFUSIVE : 'Effusive',
390 SOCIABLE : 'Sociable',
391 VERBOSE : 'Verbose',
392 TALKATIVE : 'Talkative',
393 GARRULOUS : 'Garrulous',
394 CHATTERBOX : 'Chatterbox',
395 BORING : 'Boring',
396}
397
398class GarrulousFilter(logging.Filter):
399
400 """A filter which blocks garrulous messages."""
401
402 def filter(self, record):
403 return record.levelno != GARRULOUS
404
405class VerySpecificFilter(logging.Filter):
406
407 """A filter which blocks sociable and taciturn messages."""
408
409 def filter(self, record):
410 return record.levelno not in [SOCIABLE, TACITURN]
411
412
413class CustomLevelsAndFiltersTest(BaseTest):
414
415 """Test various filtering possibilities with custom logging levels."""
416
417 # Skip the logger name group.
418 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
419
420 def setUp(self):
421 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000422 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000423 logging.addLevelName(k, v)
424
425 def log_at_all_levels(self, logger):
426 for lvl in LEVEL_RANGE:
427 logger.log(lvl, self.next_message())
428
429 def test_logger_filter(self):
430 # Filter at logger level.
431 self.root_logger.setLevel(VERBOSE)
432 # Levels >= 'Verbose' are good.
433 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000434 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000435 ('Verbose', '5'),
436 ('Sociable', '6'),
437 ('Effusive', '7'),
438 ('Terse', '8'),
439 ('Taciturn', '9'),
440 ('Silent', '10'),
441 ])
442
443 def test_handler_filter(self):
444 # Filter at handler level.
445 self.root_logger.handlers[0].setLevel(SOCIABLE)
446 try:
447 # Levels >= 'Sociable' are good.
448 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000449 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000450 ('Sociable', '6'),
451 ('Effusive', '7'),
452 ('Terse', '8'),
453 ('Taciturn', '9'),
454 ('Silent', '10'),
455 ])
456 finally:
457 self.root_logger.handlers[0].setLevel(logging.NOTSET)
458
459 def test_specific_filters(self):
460 # Set a specific filter object on the handler, and then add another
461 # filter object on the logger itself.
462 handler = self.root_logger.handlers[0]
463 specific_filter = None
464 garr = GarrulousFilter()
465 handler.addFilter(garr)
466 try:
467 self.log_at_all_levels(self.root_logger)
468 first_lines = [
469 # Notice how 'Garrulous' is missing
470 ('Boring', '1'),
471 ('Chatterbox', '2'),
472 ('Talkative', '4'),
473 ('Verbose', '5'),
474 ('Sociable', '6'),
475 ('Effusive', '7'),
476 ('Terse', '8'),
477 ('Taciturn', '9'),
478 ('Silent', '10'),
479 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000480 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000481
482 specific_filter = VerySpecificFilter()
483 self.root_logger.addFilter(specific_filter)
484 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000485 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000486 # Not only 'Garrulous' is still missing, but also 'Sociable'
487 # and 'Taciturn'
488 ('Boring', '11'),
489 ('Chatterbox', '12'),
490 ('Talkative', '14'),
491 ('Verbose', '15'),
492 ('Effusive', '17'),
493 ('Terse', '18'),
494 ('Silent', '20'),
495 ])
496 finally:
497 if specific_filter:
498 self.root_logger.removeFilter(specific_filter)
499 handler.removeFilter(garr)
500
501
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100502class HandlerTest(BaseTest):
503 def test_name(self):
504 h = logging.Handler()
505 h.name = 'generic'
506 self.assertEqual(h.name, 'generic')
507 h.name = 'anothergeneric'
508 self.assertEqual(h.name, 'anothergeneric')
509 self.assertRaises(NotImplementedError, h.emit, None)
510
Vinay Sajip5a35b062011-04-27 11:31:14 +0100511 def test_builtin_handlers(self):
512 # We can't actually *use* too many handlers in the tests,
513 # but we can try instantiating them with various options
514 if sys.platform in ('linux2', 'darwin'):
515 for existing in (True, False):
516 fd, fn = tempfile.mkstemp()
517 os.close(fd)
518 if not existing:
519 os.unlink(fn)
520 h = logging.handlers.WatchedFileHandler(fn, delay=True)
521 if existing:
522 self.assertNotEqual(h.dev, -1)
523 self.assertNotEqual(h.ino, -1)
524 else:
525 self.assertEqual(h.dev, -1)
526 self.assertEqual(h.ino, -1)
527 h.close()
528 if existing:
529 os.unlink(fn)
530 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100531 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100532 else:
533 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100534 try:
535 h = logging.handlers.SysLogHandler(sockname)
536 self.assertEqual(h.facility, h.LOG_USER)
537 self.assertTrue(h.unixsocket)
538 h.close()
539 except socket.error: # syslogd might not be available
540 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100541 h = logging.handlers.SMTPHandler('localhost', 'me', 'you', 'Log')
542 self.assertEqual(h.toaddrs, ['you'])
543 h.close()
544 for method in ('GET', 'POST', 'PUT'):
545 if method == 'PUT':
546 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
547 'localhost', '/log', method)
548 else:
549 h = logging.handlers.HTTPHandler('localhost', '/log', method)
550 h.close()
551 h = logging.handlers.BufferingHandler(0)
552 r = logging.makeLogRecord({})
553 self.assertTrue(h.shouldFlush(r))
554 h.close()
555 h = logging.handlers.BufferingHandler(1)
556 self.assertFalse(h.shouldFlush(r))
557 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100558
559class BadStream(object):
560 def write(self, data):
561 raise RuntimeError('deliberate mistake')
562
563class TestStreamHandler(logging.StreamHandler):
564 def handleError(self, record):
565 self.error_record = record
566
567class StreamHandlerTest(BaseTest):
568 def test_error_handling(self):
569 h = TestStreamHandler(BadStream())
570 r = logging.makeLogRecord({})
571 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100572 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100573 try:
574 h.handle(r)
575 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100576 h = logging.StreamHandler(BadStream())
577 sys.stderr = sio = io.StringIO()
578 h.handle(r)
579 self.assertTrue('\nRuntimeError: '
580 'deliberate mistake\n' in sio.getvalue())
581 logging.raiseExceptions = False
582 sys.stderr = sio = io.StringIO()
583 h.handle(r)
584 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100585 finally:
586 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100587 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100588
Christian Heimes180510d2008-03-03 19:15:45 +0000589class MemoryHandlerTest(BaseTest):
590
591 """Tests for the MemoryHandler."""
592
593 # Do not bother with a logger name group.
594 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
595
596 def setUp(self):
597 BaseTest.setUp(self)
598 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
599 self.root_hdlr)
600 self.mem_logger = logging.getLogger('mem')
601 self.mem_logger.propagate = 0
602 self.mem_logger.addHandler(self.mem_hdlr)
603
604 def tearDown(self):
605 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000606 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000607
608 def test_flush(self):
609 # The memory handler flushes to its target handler based on specific
610 # criteria (message count and message level).
611 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000612 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000613 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000614 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000615 # This will flush because the level is >= logging.WARNING
616 self.mem_logger.warn(self.next_message())
617 lines = [
618 ('DEBUG', '1'),
619 ('INFO', '2'),
620 ('WARNING', '3'),
621 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000622 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000623 for n in (4, 14):
624 for i in range(9):
625 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000626 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000627 # This will flush because it's the 10th message since the last
628 # flush.
629 self.mem_logger.debug(self.next_message())
630 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000631 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000632
633 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000634 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000635
636
637class ExceptionFormatter(logging.Formatter):
638 """A special exception formatter."""
639 def formatException(self, ei):
640 return "Got a [%s]" % ei[0].__name__
641
642
643class ConfigFileTest(BaseTest):
644
645 """Reading logging config from a .ini-style config file."""
646
647 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
648
649 # config0 is a standard configuration.
650 config0 = """
651 [loggers]
652 keys=root
653
654 [handlers]
655 keys=hand1
656
657 [formatters]
658 keys=form1
659
660 [logger_root]
661 level=WARNING
662 handlers=hand1
663
664 [handler_hand1]
665 class=StreamHandler
666 level=NOTSET
667 formatter=form1
668 args=(sys.stdout,)
669
670 [formatter_form1]
671 format=%(levelname)s ++ %(message)s
672 datefmt=
673 """
674
675 # config1 adds a little to the standard configuration.
676 config1 = """
677 [loggers]
678 keys=root,parser
679
680 [handlers]
681 keys=hand1
682
683 [formatters]
684 keys=form1
685
686 [logger_root]
687 level=WARNING
688 handlers=
689
690 [logger_parser]
691 level=DEBUG
692 handlers=hand1
693 propagate=1
694 qualname=compiler.parser
695
696 [handler_hand1]
697 class=StreamHandler
698 level=NOTSET
699 formatter=form1
700 args=(sys.stdout,)
701
702 [formatter_form1]
703 format=%(levelname)s ++ %(message)s
704 datefmt=
705 """
706
Vinay Sajip3f84b072011-03-07 17:49:33 +0000707 # config1a moves the handler to the root.
708 config1a = """
709 [loggers]
710 keys=root,parser
711
712 [handlers]
713 keys=hand1
714
715 [formatters]
716 keys=form1
717
718 [logger_root]
719 level=WARNING
720 handlers=hand1
721
722 [logger_parser]
723 level=DEBUG
724 handlers=
725 propagate=1
726 qualname=compiler.parser
727
728 [handler_hand1]
729 class=StreamHandler
730 level=NOTSET
731 formatter=form1
732 args=(sys.stdout,)
733
734 [formatter_form1]
735 format=%(levelname)s ++ %(message)s
736 datefmt=
737 """
738
Christian Heimes180510d2008-03-03 19:15:45 +0000739 # config2 has a subtle configuration error that should be reported
740 config2 = config1.replace("sys.stdout", "sys.stbout")
741
742 # config3 has a less subtle configuration error
743 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
744
745 # config4 specifies a custom formatter class to be loaded
746 config4 = """
747 [loggers]
748 keys=root
749
750 [handlers]
751 keys=hand1
752
753 [formatters]
754 keys=form1
755
756 [logger_root]
757 level=NOTSET
758 handlers=hand1
759
760 [handler_hand1]
761 class=StreamHandler
762 level=NOTSET
763 formatter=form1
764 args=(sys.stdout,)
765
766 [formatter_form1]
767 class=""" + __name__ + """.ExceptionFormatter
768 format=%(levelname)s:%(name)s:%(message)s
769 datefmt=
770 """
771
Georg Brandl3dbca812008-07-23 16:10:53 +0000772 # config5 specifies a custom handler class to be loaded
773 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
774
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000775 # config6 uses ', ' delimiters in the handlers and formatters sections
776 config6 = """
777 [loggers]
778 keys=root,parser
779
780 [handlers]
781 keys=hand1, hand2
782
783 [formatters]
784 keys=form1, form2
785
786 [logger_root]
787 level=WARNING
788 handlers=
789
790 [logger_parser]
791 level=DEBUG
792 handlers=hand1
793 propagate=1
794 qualname=compiler.parser
795
796 [handler_hand1]
797 class=StreamHandler
798 level=NOTSET
799 formatter=form1
800 args=(sys.stdout,)
801
802 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000803 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000804 level=NOTSET
805 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000806 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000807
808 [formatter_form1]
809 format=%(levelname)s ++ %(message)s
810 datefmt=
811
812 [formatter_form2]
813 format=%(message)s
814 datefmt=
815 """
816
Vinay Sajip3f84b072011-03-07 17:49:33 +0000817 # config7 adds a compiler logger.
818 config7 = """
819 [loggers]
820 keys=root,parser,compiler
821
822 [handlers]
823 keys=hand1
824
825 [formatters]
826 keys=form1
827
828 [logger_root]
829 level=WARNING
830 handlers=hand1
831
832 [logger_compiler]
833 level=DEBUG
834 handlers=
835 propagate=1
836 qualname=compiler
837
838 [logger_parser]
839 level=DEBUG
840 handlers=
841 propagate=1
842 qualname=compiler.parser
843
844 [handler_hand1]
845 class=StreamHandler
846 level=NOTSET
847 formatter=form1
848 args=(sys.stdout,)
849
850 [formatter_form1]
851 format=%(levelname)s ++ %(message)s
852 datefmt=
853 """
854
Christian Heimes180510d2008-03-03 19:15:45 +0000855 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000856 file = io.StringIO(textwrap.dedent(conf))
857 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000858
859 def test_config0_ok(self):
860 # A simple config file which overrides the default settings.
861 with captured_stdout() as output:
862 self.apply_config(self.config0)
863 logger = logging.getLogger()
864 # Won't output anything
865 logger.info(self.next_message())
866 # Outputs a message
867 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000868 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000869 ('ERROR', '2'),
870 ], stream=output)
871 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000872 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000873
Georg Brandl3dbca812008-07-23 16:10:53 +0000874 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000875 # A config file defining a sub-parser as well.
876 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000877 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000878 logger = logging.getLogger("compiler.parser")
879 # Both will output a message
880 logger.info(self.next_message())
881 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000882 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000883 ('INFO', '1'),
884 ('ERROR', '2'),
885 ], stream=output)
886 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000887 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000888
889 def test_config2_failure(self):
890 # A simple config file which overrides the default settings.
891 self.assertRaises(Exception, self.apply_config, self.config2)
892
893 def test_config3_failure(self):
894 # A simple config file which overrides the default settings.
895 self.assertRaises(Exception, self.apply_config, self.config3)
896
897 def test_config4_ok(self):
898 # A config file specifying a custom formatter class.
899 with captured_stdout() as output:
900 self.apply_config(self.config4)
901 logger = logging.getLogger()
902 try:
903 raise RuntimeError()
904 except RuntimeError:
905 logging.exception("just testing")
906 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000907 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000908 "ERROR:root:just testing\nGot a [RuntimeError]\n")
909 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000910 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000911
Georg Brandl3dbca812008-07-23 16:10:53 +0000912 def test_config5_ok(self):
913 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000914
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000915 def test_config6_ok(self):
916 self.test_config1_ok(config=self.config6)
917
Vinay Sajip3f84b072011-03-07 17:49:33 +0000918 def test_config7_ok(self):
919 with captured_stdout() as output:
920 self.apply_config(self.config1a)
921 logger = logging.getLogger("compiler.parser")
922 # See issue #11424. compiler-hyphenated sorts
923 # between compiler and compiler.xyz and this
924 # was preventing compiler.xyz from being included
925 # in the child loggers of compiler because of an
926 # overzealous loop termination condition.
927 hyphenated = logging.getLogger('compiler-hyphenated')
928 # All will output a message
929 logger.info(self.next_message())
930 logger.error(self.next_message())
931 hyphenated.critical(self.next_message())
932 self.assert_log_lines([
933 ('INFO', '1'),
934 ('ERROR', '2'),
935 ('CRITICAL', '3'),
936 ], stream=output)
937 # Original logger output is empty.
938 self.assert_log_lines([])
939 with captured_stdout() as output:
940 self.apply_config(self.config7)
941 logger = logging.getLogger("compiler.parser")
942 self.assertFalse(logger.disabled)
943 # Both will output a message
944 logger.info(self.next_message())
945 logger.error(self.next_message())
946 logger = logging.getLogger("compiler.lexer")
947 # Both will output a message
948 logger.info(self.next_message())
949 logger.error(self.next_message())
950 # Will not appear
951 hyphenated.critical(self.next_message())
952 self.assert_log_lines([
953 ('INFO', '4'),
954 ('ERROR', '5'),
955 ('INFO', '6'),
956 ('ERROR', '7'),
957 ], stream=output)
958 # Original logger output is empty.
959 self.assert_log_lines([])
960
Christian Heimes180510d2008-03-03 19:15:45 +0000961class LogRecordStreamHandler(StreamRequestHandler):
962
963 """Handler for a streaming logging request. It saves the log message in the
964 TCP server's 'log_output' attribute."""
965
966 TCP_LOG_END = "!!!END!!!"
967
968 def handle(self):
969 """Handle multiple requests - each expected to be of 4-byte length,
970 followed by the LogRecord in pickle format. Logs the record
971 according to whatever policy is configured locally."""
972 while True:
973 chunk = self.connection.recv(4)
974 if len(chunk) < 4:
975 break
976 slen = struct.unpack(">L", chunk)[0]
977 chunk = self.connection.recv(slen)
978 while len(chunk) < slen:
979 chunk = chunk + self.connection.recv(slen - len(chunk))
980 obj = self.unpickle(chunk)
981 record = logging.makeLogRecord(obj)
982 self.handle_log_record(record)
983
984 def unpickle(self, data):
985 return pickle.loads(data)
986
987 def handle_log_record(self, record):
988 # If the end-of-messages sentinel is seen, tell the server to
989 # terminate.
990 if self.TCP_LOG_END in record.msg:
991 self.server.abort = 1
992 return
993 self.server.log_output += record.msg + "\n"
994
Guido van Rossum376e6362003-04-25 14:22:00 +0000995
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000996class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +0000997
998 """A simple-minded TCP socket-based logging receiver suitable for test
999 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001000
1001 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +00001002 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001003
1004 def __init__(self, host='localhost',
1005 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
1006 handler=LogRecordStreamHandler):
1007 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +00001008 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +00001009 self.timeout = 0.1
1010 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001011
1012 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +00001013 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +00001014 rd, wr, ex = select.select([self.socket.fileno()], [], [],
1015 self.timeout)
1016 if rd:
1017 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +00001018 # Notify the main thread that we're about to exit
1019 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +00001020 # close the listen socket
1021 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001022
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001023
Victor Stinner45df8202010-04-28 22:31:17 +00001024@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001025class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001026
Christian Heimes180510d2008-03-03 19:15:45 +00001027 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001028
Christian Heimes180510d2008-03-03 19:15:45 +00001029 def setUp(self):
1030 """Set up a TCP server to receive log messages, and a SocketHandler
1031 pointing to that server's address and port."""
1032 BaseTest.setUp(self)
1033 self.tcpserver = LogRecordSocketReceiver(port=0)
1034 self.port = self.tcpserver.socket.getsockname()[1]
1035 self.threads = [
1036 threading.Thread(target=self.tcpserver.serve_until_stopped)]
1037 for thread in self.threads:
1038 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001039
Christian Heimes180510d2008-03-03 19:15:45 +00001040 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
1041 self.sock_hdlr.setFormatter(self.root_formatter)
1042 self.root_logger.removeHandler(self.root_logger.handlers[0])
1043 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001044
Christian Heimes180510d2008-03-03 19:15:45 +00001045 def tearDown(self):
1046 """Shutdown the TCP server."""
1047 try:
1048 self.tcpserver.abort = True
1049 del self.tcpserver
1050 self.root_logger.removeHandler(self.sock_hdlr)
1051 self.sock_hdlr.close()
1052 for thread in self.threads:
1053 thread.join(2.0)
1054 finally:
1055 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001056
Christian Heimes180510d2008-03-03 19:15:45 +00001057 def get_output(self):
1058 """Get the log output as received by the TCP server."""
1059 # Signal the TCP receiver and wait for it to terminate.
1060 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
1061 self.tcpserver.finished.wait(2.0)
1062 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001063
Christian Heimes180510d2008-03-03 19:15:45 +00001064 def test_output(self):
1065 # The log message sent to the SocketHandler is properly received.
1066 logger = logging.getLogger("tcp")
1067 logger.error("spam")
1068 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +00001069 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001070
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001071
Christian Heimes180510d2008-03-03 19:15:45 +00001072class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001073
Christian Heimes180510d2008-03-03 19:15:45 +00001074 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001075
Christian Heimes180510d2008-03-03 19:15:45 +00001076 def setUp(self):
1077 """Create a dict to remember potentially destroyed objects."""
1078 BaseTest.setUp(self)
1079 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001080
Christian Heimes180510d2008-03-03 19:15:45 +00001081 def _watch_for_survival(self, *args):
1082 """Watch the given objects for survival, by creating weakrefs to
1083 them."""
1084 for obj in args:
1085 key = id(obj), repr(obj)
1086 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001087
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001088 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001089 """Assert that all objects watched for survival have survived."""
1090 # Trigger cycle breaking.
1091 gc.collect()
1092 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001093 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001094 if ref() is None:
1095 dead.append(repr_)
1096 if dead:
1097 self.fail("%d objects should have survived "
1098 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001099
Christian Heimes180510d2008-03-03 19:15:45 +00001100 def test_persistent_loggers(self):
1101 # Logger objects are persistent and retain their configuration, even
1102 # if visible references are destroyed.
1103 self.root_logger.setLevel(logging.INFO)
1104 foo = logging.getLogger("foo")
1105 self._watch_for_survival(foo)
1106 foo.setLevel(logging.DEBUG)
1107 self.root_logger.debug(self.next_message())
1108 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001109 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001110 ('foo', 'DEBUG', '2'),
1111 ])
1112 del foo
1113 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001114 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001115 # foo has retained its settings.
1116 bar = logging.getLogger("foo")
1117 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001118 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001119 ('foo', 'DEBUG', '2'),
1120 ('foo', 'DEBUG', '3'),
1121 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001122
Benjamin Petersonf91df042009-02-13 02:50:59 +00001123
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001124class EncodingTest(BaseTest):
1125 def test_encoding_plain_file(self):
1126 # In Python 2.x, a plain file object is treated as having no encoding.
1127 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001128 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1129 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001130 # the non-ascii data we write to the log.
1131 data = "foo\x80"
1132 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001133 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001134 log.addHandler(handler)
1135 try:
1136 # write non-ascii data to the log.
1137 log.warning(data)
1138 finally:
1139 log.removeHandler(handler)
1140 handler.close()
1141 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001142 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001143 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001144 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001145 finally:
1146 f.close()
1147 finally:
1148 if os.path.isfile(fn):
1149 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001150
Benjamin Petersonf91df042009-02-13 02:50:59 +00001151 def test_encoding_cyrillic_unicode(self):
1152 log = logging.getLogger("test")
1153 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1154 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1155 #Ensure it's written in a Cyrillic encoding
1156 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001157 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001158 stream = io.BytesIO()
1159 writer = writer_class(stream, 'strict')
1160 handler = logging.StreamHandler(writer)
1161 log.addHandler(handler)
1162 try:
1163 log.warning(message)
1164 finally:
1165 log.removeHandler(handler)
1166 handler.close()
1167 # check we wrote exactly those bytes, ignoring trailing \n etc
1168 s = stream.getvalue()
1169 #Compare against what the data should be when encoded in CP-1251
1170 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1171
1172
Georg Brandlf9734072008-12-07 15:30:06 +00001173class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001174
Georg Brandlf9734072008-12-07 15:30:06 +00001175 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001176 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001177 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001178 self.addCleanup(lambda: logging.captureWarnings(False))
1179 warnings.filterwarnings("always", category=UserWarning)
1180 stream = io.StringIO()
1181 h = logging.StreamHandler(stream)
1182 logger = logging.getLogger("py.warnings")
1183 logger.addHandler(h)
1184 warnings.warn("I'm warning you...")
1185 logger.removeHandler(h)
1186 s = stream.getvalue()
1187 h.close()
1188 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001189
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001190 #See if an explicit file uses the original implementation
1191 a_file = io.StringIO()
1192 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1193 a_file, "Dummy line")
1194 s = a_file.getvalue()
1195 a_file.close()
1196 self.assertEqual(s,
1197 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1198
1199 def test_warnings_no_handlers(self):
1200 with warnings.catch_warnings():
1201 logging.captureWarnings(True)
1202 self.addCleanup(lambda: logging.captureWarnings(False))
1203
1204 # confirm our assumption: no loggers are set
1205 logger = logging.getLogger("py.warnings")
1206 assert logger.handlers == []
1207
1208 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1209 self.assertTrue(len(logger.handlers) == 1)
1210 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001211
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001212
1213def formatFunc(format, datefmt=None):
1214 return logging.Formatter(format, datefmt)
1215
1216def handlerFunc():
1217 return logging.StreamHandler()
1218
1219class CustomHandler(logging.StreamHandler):
1220 pass
1221
1222class ConfigDictTest(BaseTest):
1223
1224 """Reading logging config from a dictionary."""
1225
1226 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1227
1228 # config0 is a standard configuration.
1229 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001230 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001231 'formatters': {
1232 'form1' : {
1233 'format' : '%(levelname)s ++ %(message)s',
1234 },
1235 },
1236 'handlers' : {
1237 'hand1' : {
1238 'class' : 'logging.StreamHandler',
1239 'formatter' : 'form1',
1240 'level' : 'NOTSET',
1241 'stream' : 'ext://sys.stdout',
1242 },
1243 },
1244 'root' : {
1245 'level' : 'WARNING',
1246 'handlers' : ['hand1'],
1247 },
1248 }
1249
1250 # config1 adds a little to the standard configuration.
1251 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001252 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001253 'formatters': {
1254 'form1' : {
1255 'format' : '%(levelname)s ++ %(message)s',
1256 },
1257 },
1258 'handlers' : {
1259 'hand1' : {
1260 'class' : 'logging.StreamHandler',
1261 'formatter' : 'form1',
1262 'level' : 'NOTSET',
1263 'stream' : 'ext://sys.stdout',
1264 },
1265 },
1266 'loggers' : {
1267 'compiler.parser' : {
1268 'level' : 'DEBUG',
1269 'handlers' : ['hand1'],
1270 },
1271 },
1272 'root' : {
1273 'level' : 'WARNING',
1274 },
1275 }
1276
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001277 # config1a moves the handler to the root. Used with config8a
1278 config1a = {
1279 'version': 1,
1280 'formatters': {
1281 'form1' : {
1282 'format' : '%(levelname)s ++ %(message)s',
1283 },
1284 },
1285 'handlers' : {
1286 'hand1' : {
1287 'class' : 'logging.StreamHandler',
1288 'formatter' : 'form1',
1289 'level' : 'NOTSET',
1290 'stream' : 'ext://sys.stdout',
1291 },
1292 },
1293 'loggers' : {
1294 'compiler.parser' : {
1295 'level' : 'DEBUG',
1296 },
1297 },
1298 'root' : {
1299 'level' : 'WARNING',
1300 'handlers' : ['hand1'],
1301 },
1302 }
1303
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001304 # config2 has a subtle configuration error that should be reported
1305 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001306 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001307 'formatters': {
1308 'form1' : {
1309 'format' : '%(levelname)s ++ %(message)s',
1310 },
1311 },
1312 'handlers' : {
1313 'hand1' : {
1314 'class' : 'logging.StreamHandler',
1315 'formatter' : 'form1',
1316 'level' : 'NOTSET',
1317 'stream' : 'ext://sys.stdbout',
1318 },
1319 },
1320 'loggers' : {
1321 'compiler.parser' : {
1322 'level' : 'DEBUG',
1323 'handlers' : ['hand1'],
1324 },
1325 },
1326 'root' : {
1327 'level' : 'WARNING',
1328 },
1329 }
1330
1331 #As config1 but with a misspelt level on a handler
1332 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001333 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001334 'formatters': {
1335 'form1' : {
1336 'format' : '%(levelname)s ++ %(message)s',
1337 },
1338 },
1339 'handlers' : {
1340 'hand1' : {
1341 'class' : 'logging.StreamHandler',
1342 'formatter' : 'form1',
1343 'level' : 'NTOSET',
1344 'stream' : 'ext://sys.stdout',
1345 },
1346 },
1347 'loggers' : {
1348 'compiler.parser' : {
1349 'level' : 'DEBUG',
1350 'handlers' : ['hand1'],
1351 },
1352 },
1353 'root' : {
1354 'level' : 'WARNING',
1355 },
1356 }
1357
1358
1359 #As config1 but with a misspelt level on a logger
1360 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001361 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001362 'formatters': {
1363 'form1' : {
1364 'format' : '%(levelname)s ++ %(message)s',
1365 },
1366 },
1367 'handlers' : {
1368 'hand1' : {
1369 'class' : 'logging.StreamHandler',
1370 'formatter' : 'form1',
1371 'level' : 'NOTSET',
1372 'stream' : 'ext://sys.stdout',
1373 },
1374 },
1375 'loggers' : {
1376 'compiler.parser' : {
1377 'level' : 'DEBUG',
1378 'handlers' : ['hand1'],
1379 },
1380 },
1381 'root' : {
1382 'level' : 'WRANING',
1383 },
1384 }
1385
1386 # config3 has a less subtle configuration error
1387 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001388 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001389 'formatters': {
1390 'form1' : {
1391 'format' : '%(levelname)s ++ %(message)s',
1392 },
1393 },
1394 'handlers' : {
1395 'hand1' : {
1396 'class' : 'logging.StreamHandler',
1397 'formatter' : 'misspelled_name',
1398 'level' : 'NOTSET',
1399 'stream' : 'ext://sys.stdout',
1400 },
1401 },
1402 'loggers' : {
1403 'compiler.parser' : {
1404 'level' : 'DEBUG',
1405 'handlers' : ['hand1'],
1406 },
1407 },
1408 'root' : {
1409 'level' : 'WARNING',
1410 },
1411 }
1412
1413 # config4 specifies a custom formatter class to be loaded
1414 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001415 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001416 'formatters': {
1417 'form1' : {
1418 '()' : __name__ + '.ExceptionFormatter',
1419 'format' : '%(levelname)s:%(name)s:%(message)s',
1420 },
1421 },
1422 'handlers' : {
1423 'hand1' : {
1424 'class' : 'logging.StreamHandler',
1425 'formatter' : 'form1',
1426 'level' : 'NOTSET',
1427 'stream' : 'ext://sys.stdout',
1428 },
1429 },
1430 'root' : {
1431 'level' : 'NOTSET',
1432 'handlers' : ['hand1'],
1433 },
1434 }
1435
1436 # As config4 but using an actual callable rather than a string
1437 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001438 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001439 'formatters': {
1440 'form1' : {
1441 '()' : ExceptionFormatter,
1442 'format' : '%(levelname)s:%(name)s:%(message)s',
1443 },
1444 'form2' : {
1445 '()' : __name__ + '.formatFunc',
1446 'format' : '%(levelname)s:%(name)s:%(message)s',
1447 },
1448 'form3' : {
1449 '()' : formatFunc,
1450 'format' : '%(levelname)s:%(name)s:%(message)s',
1451 },
1452 },
1453 'handlers' : {
1454 'hand1' : {
1455 'class' : 'logging.StreamHandler',
1456 'formatter' : 'form1',
1457 'level' : 'NOTSET',
1458 'stream' : 'ext://sys.stdout',
1459 },
1460 'hand2' : {
1461 '()' : handlerFunc,
1462 },
1463 },
1464 'root' : {
1465 'level' : 'NOTSET',
1466 'handlers' : ['hand1'],
1467 },
1468 }
1469
1470 # config5 specifies a custom handler class to be loaded
1471 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001472 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001473 'formatters': {
1474 'form1' : {
1475 'format' : '%(levelname)s ++ %(message)s',
1476 },
1477 },
1478 'handlers' : {
1479 'hand1' : {
1480 'class' : __name__ + '.CustomHandler',
1481 'formatter' : 'form1',
1482 'level' : 'NOTSET',
1483 'stream' : 'ext://sys.stdout',
1484 },
1485 },
1486 'loggers' : {
1487 'compiler.parser' : {
1488 'level' : 'DEBUG',
1489 'handlers' : ['hand1'],
1490 },
1491 },
1492 'root' : {
1493 'level' : 'WARNING',
1494 },
1495 }
1496
1497 # config6 specifies a custom handler class to be loaded
1498 # but has bad arguments
1499 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001500 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001501 'formatters': {
1502 'form1' : {
1503 'format' : '%(levelname)s ++ %(message)s',
1504 },
1505 },
1506 'handlers' : {
1507 'hand1' : {
1508 'class' : __name__ + '.CustomHandler',
1509 'formatter' : 'form1',
1510 'level' : 'NOTSET',
1511 'stream' : 'ext://sys.stdout',
1512 '9' : 'invalid parameter name',
1513 },
1514 },
1515 'loggers' : {
1516 'compiler.parser' : {
1517 'level' : 'DEBUG',
1518 'handlers' : ['hand1'],
1519 },
1520 },
1521 'root' : {
1522 'level' : 'WARNING',
1523 },
1524 }
1525
1526 #config 7 does not define compiler.parser but defines compiler.lexer
1527 #so compiler.parser should be disabled after applying it
1528 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001529 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001530 'formatters': {
1531 'form1' : {
1532 'format' : '%(levelname)s ++ %(message)s',
1533 },
1534 },
1535 'handlers' : {
1536 'hand1' : {
1537 'class' : 'logging.StreamHandler',
1538 'formatter' : 'form1',
1539 'level' : 'NOTSET',
1540 'stream' : 'ext://sys.stdout',
1541 },
1542 },
1543 'loggers' : {
1544 'compiler.lexer' : {
1545 'level' : 'DEBUG',
1546 'handlers' : ['hand1'],
1547 },
1548 },
1549 'root' : {
1550 'level' : 'WARNING',
1551 },
1552 }
1553
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001554 # config8 defines both compiler and compiler.lexer
1555 # so compiler.parser should not be disabled (since
1556 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001557 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001558 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001559 'disable_existing_loggers' : False,
1560 'formatters': {
1561 'form1' : {
1562 'format' : '%(levelname)s ++ %(message)s',
1563 },
1564 },
1565 'handlers' : {
1566 'hand1' : {
1567 'class' : 'logging.StreamHandler',
1568 'formatter' : 'form1',
1569 'level' : 'NOTSET',
1570 'stream' : 'ext://sys.stdout',
1571 },
1572 },
1573 'loggers' : {
1574 'compiler' : {
1575 'level' : 'DEBUG',
1576 'handlers' : ['hand1'],
1577 },
1578 'compiler.lexer' : {
1579 },
1580 },
1581 'root' : {
1582 'level' : 'WARNING',
1583 },
1584 }
1585
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001586 # config8a disables existing loggers
1587 config8a = {
1588 'version': 1,
1589 'disable_existing_loggers' : True,
1590 'formatters': {
1591 'form1' : {
1592 'format' : '%(levelname)s ++ %(message)s',
1593 },
1594 },
1595 'handlers' : {
1596 'hand1' : {
1597 'class' : 'logging.StreamHandler',
1598 'formatter' : 'form1',
1599 'level' : 'NOTSET',
1600 'stream' : 'ext://sys.stdout',
1601 },
1602 },
1603 'loggers' : {
1604 'compiler' : {
1605 'level' : 'DEBUG',
1606 'handlers' : ['hand1'],
1607 },
1608 'compiler.lexer' : {
1609 },
1610 },
1611 'root' : {
1612 'level' : 'WARNING',
1613 },
1614 }
1615
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001616 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001617 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001618 'formatters': {
1619 'form1' : {
1620 'format' : '%(levelname)s ++ %(message)s',
1621 },
1622 },
1623 'handlers' : {
1624 'hand1' : {
1625 'class' : 'logging.StreamHandler',
1626 'formatter' : 'form1',
1627 'level' : 'WARNING',
1628 'stream' : 'ext://sys.stdout',
1629 },
1630 },
1631 'loggers' : {
1632 'compiler.parser' : {
1633 'level' : 'WARNING',
1634 'handlers' : ['hand1'],
1635 },
1636 },
1637 'root' : {
1638 'level' : 'NOTSET',
1639 },
1640 }
1641
1642 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001643 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001644 'incremental' : True,
1645 'handlers' : {
1646 'hand1' : {
1647 'level' : 'WARNING',
1648 },
1649 },
1650 'loggers' : {
1651 'compiler.parser' : {
1652 'level' : 'INFO',
1653 },
1654 },
1655 }
1656
1657 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001658 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001659 'incremental' : True,
1660 'handlers' : {
1661 'hand1' : {
1662 'level' : 'INFO',
1663 },
1664 },
1665 'loggers' : {
1666 'compiler.parser' : {
1667 'level' : 'INFO',
1668 },
1669 },
1670 }
1671
1672 #As config1 but with a filter added
1673 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001674 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001675 'formatters': {
1676 'form1' : {
1677 'format' : '%(levelname)s ++ %(message)s',
1678 },
1679 },
1680 'filters' : {
1681 'filt1' : {
1682 'name' : 'compiler.parser',
1683 },
1684 },
1685 'handlers' : {
1686 'hand1' : {
1687 'class' : 'logging.StreamHandler',
1688 'formatter' : 'form1',
1689 'level' : 'NOTSET',
1690 'stream' : 'ext://sys.stdout',
1691 'filters' : ['filt1'],
1692 },
1693 },
1694 'loggers' : {
1695 'compiler.parser' : {
1696 'level' : 'DEBUG',
1697 'filters' : ['filt1'],
1698 },
1699 },
1700 'root' : {
1701 'level' : 'WARNING',
1702 'handlers' : ['hand1'],
1703 },
1704 }
1705
1706 #As config1 but using cfg:// references
1707 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001708 'version': 1,
1709 'true_formatters': {
1710 'form1' : {
1711 'format' : '%(levelname)s ++ %(message)s',
1712 },
1713 },
1714 'handler_configs': {
1715 'hand1' : {
1716 'class' : 'logging.StreamHandler',
1717 'formatter' : 'form1',
1718 'level' : 'NOTSET',
1719 'stream' : 'ext://sys.stdout',
1720 },
1721 },
1722 'formatters' : 'cfg://true_formatters',
1723 'handlers' : {
1724 'hand1' : 'cfg://handler_configs[hand1]',
1725 },
1726 'loggers' : {
1727 'compiler.parser' : {
1728 'level' : 'DEBUG',
1729 'handlers' : ['hand1'],
1730 },
1731 },
1732 'root' : {
1733 'level' : 'WARNING',
1734 },
1735 }
1736
1737 #As config11 but missing the version key
1738 config12 = {
1739 'true_formatters': {
1740 'form1' : {
1741 'format' : '%(levelname)s ++ %(message)s',
1742 },
1743 },
1744 'handler_configs': {
1745 'hand1' : {
1746 'class' : 'logging.StreamHandler',
1747 'formatter' : 'form1',
1748 'level' : 'NOTSET',
1749 'stream' : 'ext://sys.stdout',
1750 },
1751 },
1752 'formatters' : 'cfg://true_formatters',
1753 'handlers' : {
1754 'hand1' : 'cfg://handler_configs[hand1]',
1755 },
1756 'loggers' : {
1757 'compiler.parser' : {
1758 'level' : 'DEBUG',
1759 'handlers' : ['hand1'],
1760 },
1761 },
1762 'root' : {
1763 'level' : 'WARNING',
1764 },
1765 }
1766
1767 #As config11 but using an unsupported version
1768 config13 = {
1769 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001770 'true_formatters': {
1771 'form1' : {
1772 'format' : '%(levelname)s ++ %(message)s',
1773 },
1774 },
1775 'handler_configs': {
1776 'hand1' : {
1777 'class' : 'logging.StreamHandler',
1778 'formatter' : 'form1',
1779 'level' : 'NOTSET',
1780 'stream' : 'ext://sys.stdout',
1781 },
1782 },
1783 'formatters' : 'cfg://true_formatters',
1784 'handlers' : {
1785 'hand1' : 'cfg://handler_configs[hand1]',
1786 },
1787 'loggers' : {
1788 'compiler.parser' : {
1789 'level' : 'DEBUG',
1790 'handlers' : ['hand1'],
1791 },
1792 },
1793 'root' : {
1794 'level' : 'WARNING',
1795 },
1796 }
1797
1798 def apply_config(self, conf):
1799 logging.config.dictConfig(conf)
1800
1801 def test_config0_ok(self):
1802 # A simple config which overrides the default settings.
1803 with captured_stdout() as output:
1804 self.apply_config(self.config0)
1805 logger = logging.getLogger()
1806 # Won't output anything
1807 logger.info(self.next_message())
1808 # Outputs a message
1809 logger.error(self.next_message())
1810 self.assert_log_lines([
1811 ('ERROR', '2'),
1812 ], stream=output)
1813 # Original logger output is empty.
1814 self.assert_log_lines([])
1815
1816 def test_config1_ok(self, config=config1):
1817 # A config defining a sub-parser as well.
1818 with captured_stdout() as output:
1819 self.apply_config(config)
1820 logger = logging.getLogger("compiler.parser")
1821 # Both will output a message
1822 logger.info(self.next_message())
1823 logger.error(self.next_message())
1824 self.assert_log_lines([
1825 ('INFO', '1'),
1826 ('ERROR', '2'),
1827 ], stream=output)
1828 # Original logger output is empty.
1829 self.assert_log_lines([])
1830
1831 def test_config2_failure(self):
1832 # A simple config which overrides the default settings.
1833 self.assertRaises(Exception, self.apply_config, self.config2)
1834
1835 def test_config2a_failure(self):
1836 # A simple config which overrides the default settings.
1837 self.assertRaises(Exception, self.apply_config, self.config2a)
1838
1839 def test_config2b_failure(self):
1840 # A simple config which overrides the default settings.
1841 self.assertRaises(Exception, self.apply_config, self.config2b)
1842
1843 def test_config3_failure(self):
1844 # A simple config which overrides the default settings.
1845 self.assertRaises(Exception, self.apply_config, self.config3)
1846
1847 def test_config4_ok(self):
1848 # A config specifying a custom formatter class.
1849 with captured_stdout() as output:
1850 self.apply_config(self.config4)
1851 #logger = logging.getLogger()
1852 try:
1853 raise RuntimeError()
1854 except RuntimeError:
1855 logging.exception("just testing")
1856 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001857 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001858 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1859 # Original logger output is empty
1860 self.assert_log_lines([])
1861
1862 def test_config4a_ok(self):
1863 # A config specifying a custom formatter class.
1864 with captured_stdout() as output:
1865 self.apply_config(self.config4a)
1866 #logger = logging.getLogger()
1867 try:
1868 raise RuntimeError()
1869 except RuntimeError:
1870 logging.exception("just testing")
1871 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001872 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001873 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1874 # Original logger output is empty
1875 self.assert_log_lines([])
1876
1877 def test_config5_ok(self):
1878 self.test_config1_ok(config=self.config5)
1879
1880 def test_config6_failure(self):
1881 self.assertRaises(Exception, self.apply_config, self.config6)
1882
1883 def test_config7_ok(self):
1884 with captured_stdout() as output:
1885 self.apply_config(self.config1)
1886 logger = logging.getLogger("compiler.parser")
1887 # Both will output a message
1888 logger.info(self.next_message())
1889 logger.error(self.next_message())
1890 self.assert_log_lines([
1891 ('INFO', '1'),
1892 ('ERROR', '2'),
1893 ], stream=output)
1894 # Original logger output is empty.
1895 self.assert_log_lines([])
1896 with captured_stdout() as output:
1897 self.apply_config(self.config7)
1898 logger = logging.getLogger("compiler.parser")
1899 self.assertTrue(logger.disabled)
1900 logger = logging.getLogger("compiler.lexer")
1901 # Both will output a message
1902 logger.info(self.next_message())
1903 logger.error(self.next_message())
1904 self.assert_log_lines([
1905 ('INFO', '3'),
1906 ('ERROR', '4'),
1907 ], stream=output)
1908 # Original logger output is empty.
1909 self.assert_log_lines([])
1910
1911 #Same as test_config_7_ok but don't disable old loggers.
1912 def test_config_8_ok(self):
1913 with captured_stdout() as output:
1914 self.apply_config(self.config1)
1915 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001916 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001917 logger.info(self.next_message())
1918 logger.error(self.next_message())
1919 self.assert_log_lines([
1920 ('INFO', '1'),
1921 ('ERROR', '2'),
1922 ], stream=output)
1923 # Original logger output is empty.
1924 self.assert_log_lines([])
1925 with captured_stdout() as output:
1926 self.apply_config(self.config8)
1927 logger = logging.getLogger("compiler.parser")
1928 self.assertFalse(logger.disabled)
1929 # Both will output a message
1930 logger.info(self.next_message())
1931 logger.error(self.next_message())
1932 logger = logging.getLogger("compiler.lexer")
1933 # Both will output a message
1934 logger.info(self.next_message())
1935 logger.error(self.next_message())
1936 self.assert_log_lines([
1937 ('INFO', '3'),
1938 ('ERROR', '4'),
1939 ('INFO', '5'),
1940 ('ERROR', '6'),
1941 ], stream=output)
1942 # Original logger output is empty.
1943 self.assert_log_lines([])
1944
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001945 def test_config_8a_ok(self):
1946 with captured_stdout() as output:
1947 self.apply_config(self.config1a)
1948 logger = logging.getLogger("compiler.parser")
1949 # See issue #11424. compiler-hyphenated sorts
1950 # between compiler and compiler.xyz and this
1951 # was preventing compiler.xyz from being included
1952 # in the child loggers of compiler because of an
1953 # overzealous loop termination condition.
1954 hyphenated = logging.getLogger('compiler-hyphenated')
1955 # All will output a message
1956 logger.info(self.next_message())
1957 logger.error(self.next_message())
1958 hyphenated.critical(self.next_message())
1959 self.assert_log_lines([
1960 ('INFO', '1'),
1961 ('ERROR', '2'),
1962 ('CRITICAL', '3'),
1963 ], stream=output)
1964 # Original logger output is empty.
1965 self.assert_log_lines([])
1966 with captured_stdout() as output:
1967 self.apply_config(self.config8a)
1968 logger = logging.getLogger("compiler.parser")
1969 self.assertFalse(logger.disabled)
1970 # Both will output a message
1971 logger.info(self.next_message())
1972 logger.error(self.next_message())
1973 logger = logging.getLogger("compiler.lexer")
1974 # Both will output a message
1975 logger.info(self.next_message())
1976 logger.error(self.next_message())
1977 # Will not appear
1978 hyphenated.critical(self.next_message())
1979 self.assert_log_lines([
1980 ('INFO', '4'),
1981 ('ERROR', '5'),
1982 ('INFO', '6'),
1983 ('ERROR', '7'),
1984 ], stream=output)
1985 # Original logger output is empty.
1986 self.assert_log_lines([])
1987
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001988 def test_config_9_ok(self):
1989 with captured_stdout() as output:
1990 self.apply_config(self.config9)
1991 logger = logging.getLogger("compiler.parser")
1992 #Nothing will be output since both handler and logger are set to WARNING
1993 logger.info(self.next_message())
1994 self.assert_log_lines([], stream=output)
1995 self.apply_config(self.config9a)
1996 #Nothing will be output since both handler is still set to WARNING
1997 logger.info(self.next_message())
1998 self.assert_log_lines([], stream=output)
1999 self.apply_config(self.config9b)
2000 #Message should now be output
2001 logger.info(self.next_message())
2002 self.assert_log_lines([
2003 ('INFO', '3'),
2004 ], stream=output)
2005
2006 def test_config_10_ok(self):
2007 with captured_stdout() as output:
2008 self.apply_config(self.config10)
2009 logger = logging.getLogger("compiler.parser")
2010 logger.warning(self.next_message())
2011 logger = logging.getLogger('compiler')
2012 #Not output, because filtered
2013 logger.warning(self.next_message())
2014 logger = logging.getLogger('compiler.lexer')
2015 #Not output, because filtered
2016 logger.warning(self.next_message())
2017 logger = logging.getLogger("compiler.parser.codegen")
2018 #Output, as not filtered
2019 logger.error(self.next_message())
2020 self.assert_log_lines([
2021 ('WARNING', '1'),
2022 ('ERROR', '4'),
2023 ], stream=output)
2024
2025 def test_config11_ok(self):
2026 self.test_config1_ok(self.config11)
2027
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002028 def test_config12_failure(self):
2029 self.assertRaises(Exception, self.apply_config, self.config12)
2030
2031 def test_config13_failure(self):
2032 self.assertRaises(Exception, self.apply_config, self.config13)
2033
Victor Stinner45df8202010-04-28 22:31:17 +00002034 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002035 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002036 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002037 # Ask for a randomly assigned port (by using port 0)
2038 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002039 t.start()
2040 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002041 # Now get the port allocated
2042 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002043 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002044 try:
2045 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2046 sock.settimeout(2.0)
2047 sock.connect(('localhost', port))
2048
2049 slen = struct.pack('>L', len(text))
2050 s = slen + text
2051 sentsofar = 0
2052 left = len(s)
2053 while left > 0:
2054 sent = sock.send(s[sentsofar:])
2055 sentsofar += sent
2056 left -= sent
2057 sock.close()
2058 finally:
2059 t.ready.wait(2.0)
2060 logging.config.stopListening()
2061 t.join(2.0)
2062
2063 def test_listen_config_10_ok(self):
2064 with captured_stdout() as output:
2065 self.setup_via_listener(json.dumps(self.config10))
2066 logger = logging.getLogger("compiler.parser")
2067 logger.warning(self.next_message())
2068 logger = logging.getLogger('compiler')
2069 #Not output, because filtered
2070 logger.warning(self.next_message())
2071 logger = logging.getLogger('compiler.lexer')
2072 #Not output, because filtered
2073 logger.warning(self.next_message())
2074 logger = logging.getLogger("compiler.parser.codegen")
2075 #Output, as not filtered
2076 logger.error(self.next_message())
2077 self.assert_log_lines([
2078 ('WARNING', '1'),
2079 ('ERROR', '4'),
2080 ], stream=output)
2081
2082 def test_listen_config_1_ok(self):
2083 with captured_stdout() as output:
2084 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2085 logger = logging.getLogger("compiler.parser")
2086 # Both will output a message
2087 logger.info(self.next_message())
2088 logger.error(self.next_message())
2089 self.assert_log_lines([
2090 ('INFO', '1'),
2091 ('ERROR', '2'),
2092 ], stream=output)
2093 # Original logger output is empty.
2094 self.assert_log_lines([])
2095
Vinay Sajip373baef2011-04-26 20:05:24 +01002096 def test_baseconfig(self):
2097 d = {
2098 'atuple': (1, 2, 3),
2099 'alist': ['a', 'b', 'c'],
2100 'adict': {'d': 'e', 'f': 3 },
2101 'nest1': ('g', ('h', 'i'), 'j'),
2102 'nest2': ['k', ['l', 'm'], 'n'],
2103 'nest3': ['o', 'cfg://alist', 'p'],
2104 }
2105 bc = logging.config.BaseConfigurator(d)
2106 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2107 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2108 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2109 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2110 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2111 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2112 v = bc.convert('cfg://nest3')
2113 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2114 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2115 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2116 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002117
2118class ManagerTest(BaseTest):
2119 def test_manager_loggerclass(self):
2120 logged = []
2121
2122 class MyLogger(logging.Logger):
2123 def _log(self, level, msg, args, exc_info=None, extra=None):
2124 logged.append(msg)
2125
2126 man = logging.Manager(None)
2127 self.assertRaises(TypeError, man.setLoggerClass, int)
2128 man.setLoggerClass(MyLogger)
2129 logger = man.getLogger('test')
2130 logger.warning('should appear in logged')
2131 logging.warning('should not appear in logged')
2132
2133 self.assertEqual(logged, ['should appear in logged'])
2134
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002135 def test_set_log_record_factory(self):
2136 man = logging.Manager(None)
2137 expected = object()
2138 man.setLogRecordFactory(expected)
2139 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002140
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002141class ChildLoggerTest(BaseTest):
2142 def test_child_loggers(self):
2143 r = logging.getLogger()
2144 l1 = logging.getLogger('abc')
2145 l2 = logging.getLogger('def.ghi')
2146 c1 = r.getChild('xyz')
2147 c2 = r.getChild('uvw.xyz')
2148 self.assertTrue(c1 is logging.getLogger('xyz'))
2149 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2150 c1 = l1.getChild('def')
2151 c2 = c1.getChild('ghi')
2152 c3 = l1.getChild('def.ghi')
2153 self.assertTrue(c1 is logging.getLogger('abc.def'))
2154 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2155 self.assertTrue(c2 is c3)
2156
2157
Vinay Sajip6fac8172010-10-19 20:44:14 +00002158class DerivedLogRecord(logging.LogRecord):
2159 pass
2160
Vinay Sajip61561522010-12-03 11:50:38 +00002161class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002162
2163 def setUp(self):
2164 class CheckingFilter(logging.Filter):
2165 def __init__(self, cls):
2166 self.cls = cls
2167
2168 def filter(self, record):
2169 t = type(record)
2170 if t is not self.cls:
2171 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2172 self.cls)
2173 raise TypeError(msg)
2174 return True
2175
2176 BaseTest.setUp(self)
2177 self.filter = CheckingFilter(DerivedLogRecord)
2178 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002179 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002180
2181 def tearDown(self):
2182 self.root_logger.removeFilter(self.filter)
2183 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002184 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002185
2186 def test_logrecord_class(self):
2187 self.assertRaises(TypeError, self.root_logger.warning,
2188 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002189 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002190 self.root_logger.error(self.next_message())
2191 self.assert_log_lines([
2192 ('root', 'ERROR', '2'),
2193 ])
2194
2195
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002196class QueueHandlerTest(BaseTest):
2197 # Do not bother with a logger name group.
2198 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2199
2200 def setUp(self):
2201 BaseTest.setUp(self)
2202 self.queue = queue.Queue(-1)
2203 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2204 self.que_logger = logging.getLogger('que')
2205 self.que_logger.propagate = False
2206 self.que_logger.setLevel(logging.WARNING)
2207 self.que_logger.addHandler(self.que_hdlr)
2208
2209 def tearDown(self):
2210 self.que_hdlr.close()
2211 BaseTest.tearDown(self)
2212
2213 def test_queue_handler(self):
2214 self.que_logger.debug(self.next_message())
2215 self.assertRaises(queue.Empty, self.queue.get_nowait)
2216 self.que_logger.info(self.next_message())
2217 self.assertRaises(queue.Empty, self.queue.get_nowait)
2218 msg = self.next_message()
2219 self.que_logger.warning(msg)
2220 data = self.queue.get_nowait()
2221 self.assertTrue(isinstance(data, logging.LogRecord))
2222 self.assertEqual(data.name, self.que_logger.name)
2223 self.assertEqual((data.msg, data.args), (msg, None))
2224
Vinay Sajipe723e962011-04-15 22:27:17 +01002225 def test_queue_listener(self):
2226 handler = TestHandler(Matcher())
2227 listener = logging.handlers.QueueListener(self.queue, handler)
2228 listener.start()
2229 try:
2230 self.que_logger.warning(self.next_message())
2231 self.que_logger.error(self.next_message())
2232 self.que_logger.critical(self.next_message())
2233 finally:
2234 listener.stop()
2235 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2236 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2237 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2238
Vinay Sajip37eb3382011-04-26 20:26:41 +01002239ZERO = datetime.timedelta(0)
2240
2241class UTC(datetime.tzinfo):
2242 def utcoffset(self, dt):
2243 return ZERO
2244
2245 dst = utcoffset
2246
2247 def tzname(self, dt):
2248 return 'UTC'
2249
2250utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002251
Vinay Sajipa39c5712010-10-25 13:57:39 +00002252class FormatterTest(unittest.TestCase):
2253 def setUp(self):
2254 self.common = {
2255 'name': 'formatter.test',
2256 'level': logging.DEBUG,
2257 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2258 'lineno': 42,
2259 'exc_info': None,
2260 'func': None,
2261 'msg': 'Message with %d %s',
2262 'args': (2, 'placeholders'),
2263 }
2264 self.variants = {
2265 }
2266
2267 def get_record(self, name=None):
2268 result = dict(self.common)
2269 if name is not None:
2270 result.update(self.variants[name])
2271 return logging.makeLogRecord(result)
2272
2273 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002274 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002275 r = self.get_record()
2276 f = logging.Formatter('${%(message)s}')
2277 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2278 f = logging.Formatter('%(random)s')
2279 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002280 self.assertFalse(f.usesTime())
2281 f = logging.Formatter('%(asctime)s')
2282 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002283 f = logging.Formatter('%(asctime)-15s')
2284 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002285 f = logging.Formatter('asctime')
2286 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002287
2288 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002289 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002290 r = self.get_record()
2291 f = logging.Formatter('$%{message}%$', style='{')
2292 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2293 f = logging.Formatter('{random}', style='{')
2294 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002295 self.assertFalse(f.usesTime())
2296 f = logging.Formatter('{asctime}', style='{')
2297 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002298 f = logging.Formatter('{asctime!s:15}', style='{')
2299 self.assertTrue(f.usesTime())
2300 f = logging.Formatter('{asctime:15}', style='{')
2301 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002302 f = logging.Formatter('asctime', style='{')
2303 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002304
2305 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002306 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002307 r = self.get_record()
2308 f = logging.Formatter('$message', style='$')
2309 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2310 f = logging.Formatter('$$%${message}%$$', style='$')
2311 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2312 f = logging.Formatter('${random}', style='$')
2313 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002314 self.assertFalse(f.usesTime())
2315 f = logging.Formatter('${asctime}', style='$')
2316 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002317 f = logging.Formatter('${asctime', style='$')
2318 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002319 f = logging.Formatter('$asctime', style='$')
2320 self.assertTrue(f.usesTime())
2321 f = logging.Formatter('asctime', style='$')
2322 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002323
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002324 def test_invalid_style(self):
2325 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2326
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002327 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002328 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002329 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002330 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002331 r.msecs = 123
2332 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002333 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002334 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2335 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002336 f.format(r)
2337 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2338
2339class TestBufferingFormatter(logging.BufferingFormatter):
2340 def formatHeader(self, records):
2341 return '[(%d)' % len(records)
2342
2343 def formatFooter(self, records):
2344 return '(%d)]' % len(records)
2345
2346class BufferingFormatterTest(unittest.TestCase):
2347 def setUp(self):
2348 self.records = [
2349 logging.makeLogRecord({'msg': 'one'}),
2350 logging.makeLogRecord({'msg': 'two'}),
2351 ]
2352
2353 def test_default(self):
2354 f = logging.BufferingFormatter()
2355 self.assertEqual('', f.format([]))
2356 self.assertEqual('onetwo', f.format(self.records))
2357
2358 def test_custom(self):
2359 f = TestBufferingFormatter()
2360 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2361 lf = logging.Formatter('<%(message)s>')
2362 f = TestBufferingFormatter(lf)
2363 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002364
2365class ExceptionTest(BaseTest):
2366 def test_formatting(self):
2367 r = self.root_logger
2368 h = RecordingHandler()
2369 r.addHandler(h)
2370 try:
2371 raise RuntimeError('deliberate mistake')
2372 except:
2373 logging.exception('failed', stack_info=True)
2374 r.removeHandler(h)
2375 h.close()
2376 r = h.records[0]
2377 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2378 'call last):\n'))
2379 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2380 'deliberate mistake'))
2381 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2382 'call last):\n'))
2383 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2384 'stack_info=True)'))
2385
2386
Vinay Sajip5a27d402010-12-10 11:42:57 +00002387class LastResortTest(BaseTest):
2388 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002389 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002390 root = self.root_logger
2391 root.removeHandler(self.root_hdlr)
2392 old_stderr = sys.stderr
2393 old_lastresort = logging.lastResort
2394 old_raise_exceptions = logging.raiseExceptions
2395 try:
2396 sys.stderr = sio = io.StringIO()
2397 root.warning('This is your final chance!')
2398 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2399 #No handlers and no last resort, so 'No handlers' message
2400 logging.lastResort = None
2401 sys.stderr = sio = io.StringIO()
2402 root.warning('This is your final chance!')
2403 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2404 # 'No handlers' message only printed once
2405 sys.stderr = sio = io.StringIO()
2406 root.warning('This is your final chance!')
2407 self.assertEqual(sio.getvalue(), '')
2408 root.manager.emittedNoHandlerWarning = False
2409 #If raiseExceptions is False, no message is printed
2410 logging.raiseExceptions = False
2411 sys.stderr = sio = io.StringIO()
2412 root.warning('This is your final chance!')
2413 self.assertEqual(sio.getvalue(), '')
2414 finally:
2415 sys.stderr = old_stderr
2416 root.addHandler(self.root_hdlr)
2417 logging.lastResort = old_lastresort
2418 logging.raiseExceptions = old_raise_exceptions
2419
2420
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002421class FakeHandler:
2422
2423 def __init__(self, identifier, called):
2424 for method in ('acquire', 'flush', 'close', 'release'):
2425 setattr(self, method, self.record_call(identifier, method, called))
2426
2427 def record_call(self, identifier, method_name, called):
2428 def inner():
2429 called.append('{} - {}'.format(identifier, method_name))
2430 return inner
2431
2432
2433class RecordingHandler(logging.NullHandler):
2434
2435 def __init__(self, *args, **kwargs):
2436 super(RecordingHandler, self).__init__(*args, **kwargs)
2437 self.records = []
2438
2439 def handle(self, record):
2440 """Keep track of all the emitted records."""
2441 self.records.append(record)
2442
2443
2444class ShutdownTest(BaseTest):
2445
Vinay Sajip5e66b162011-04-20 15:41:14 +01002446 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002447
2448 def setUp(self):
2449 super(ShutdownTest, self).setUp()
2450 self.called = []
2451
2452 raise_exceptions = logging.raiseExceptions
2453 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2454
2455 def raise_error(self, error):
2456 def inner():
2457 raise error()
2458 return inner
2459
2460 def test_no_failure(self):
2461 # create some fake handlers
2462 handler0 = FakeHandler(0, self.called)
2463 handler1 = FakeHandler(1, self.called)
2464 handler2 = FakeHandler(2, self.called)
2465
2466 # create live weakref to those handlers
2467 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2468
2469 logging.shutdown(handlerList=list(handlers))
2470
2471 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2472 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2473 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2474 self.assertEqual(expected, self.called)
2475
2476 def _test_with_failure_in_method(self, method, error):
2477 handler = FakeHandler(0, self.called)
2478 setattr(handler, method, self.raise_error(error))
2479 handlers = [logging.weakref.ref(handler)]
2480
2481 logging.shutdown(handlerList=list(handlers))
2482
2483 self.assertEqual('0 - release', self.called[-1])
2484
2485 def test_with_ioerror_in_acquire(self):
2486 self._test_with_failure_in_method('acquire', IOError)
2487
2488 def test_with_ioerror_in_flush(self):
2489 self._test_with_failure_in_method('flush', IOError)
2490
2491 def test_with_ioerror_in_close(self):
2492 self._test_with_failure_in_method('close', IOError)
2493
2494 def test_with_valueerror_in_acquire(self):
2495 self._test_with_failure_in_method('acquire', ValueError)
2496
2497 def test_with_valueerror_in_flush(self):
2498 self._test_with_failure_in_method('flush', ValueError)
2499
2500 def test_with_valueerror_in_close(self):
2501 self._test_with_failure_in_method('close', ValueError)
2502
2503 def test_with_other_error_in_acquire_without_raise(self):
2504 logging.raiseExceptions = False
2505 self._test_with_failure_in_method('acquire', IndexError)
2506
2507 def test_with_other_error_in_flush_without_raise(self):
2508 logging.raiseExceptions = False
2509 self._test_with_failure_in_method('flush', IndexError)
2510
2511 def test_with_other_error_in_close_without_raise(self):
2512 logging.raiseExceptions = False
2513 self._test_with_failure_in_method('close', IndexError)
2514
2515 def test_with_other_error_in_acquire_with_raise(self):
2516 logging.raiseExceptions = True
2517 self.assertRaises(IndexError, self._test_with_failure_in_method,
2518 'acquire', IndexError)
2519
2520 def test_with_other_error_in_flush_with_raise(self):
2521 logging.raiseExceptions = True
2522 self.assertRaises(IndexError, self._test_with_failure_in_method,
2523 'flush', IndexError)
2524
2525 def test_with_other_error_in_close_with_raise(self):
2526 logging.raiseExceptions = True
2527 self.assertRaises(IndexError, self._test_with_failure_in_method,
2528 'close', IndexError)
2529
2530
2531class ModuleLevelMiscTest(BaseTest):
2532
Vinay Sajip5e66b162011-04-20 15:41:14 +01002533 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002534
2535 def test_disable(self):
2536 old_disable = logging.root.manager.disable
2537 # confirm our assumptions are correct
2538 assert old_disable == 0
2539 self.addCleanup(lambda: logging.disable(old_disable))
2540
2541 logging.disable(83)
2542 self.assertEqual(logging.root.manager.disable, 83)
2543
2544 def _test_log(self, method, level=None):
2545 called = []
2546 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002547 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002548
2549 recording = RecordingHandler()
2550 logging.root.addHandler(recording)
2551
2552 log_method = getattr(logging, method)
2553 if level is not None:
2554 log_method(level, "test me: %r", recording)
2555 else:
2556 log_method("test me: %r", recording)
2557
2558 self.assertEqual(len(recording.records), 1)
2559 record = recording.records[0]
2560 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2561
2562 expected_level = level if level is not None else getattr(logging, method.upper())
2563 self.assertEqual(record.levelno, expected_level)
2564
2565 # basicConfig was not called!
2566 self.assertEqual(called, [])
2567
2568 def test_log(self):
2569 self._test_log('log', logging.ERROR)
2570
2571 def test_debug(self):
2572 self._test_log('debug')
2573
2574 def test_info(self):
2575 self._test_log('info')
2576
2577 def test_warning(self):
2578 self._test_log('warning')
2579
2580 def test_error(self):
2581 self._test_log('error')
2582
2583 def test_critical(self):
2584 self._test_log('critical')
2585
2586 def test_set_logger_class(self):
2587 self.assertRaises(TypeError, logging.setLoggerClass, object)
2588
2589 class MyLogger(logging.Logger):
2590 pass
2591
2592 logging.setLoggerClass(MyLogger)
2593 self.assertEqual(logging.getLoggerClass(), MyLogger)
2594
2595 logging.setLoggerClass(logging.Logger)
2596 self.assertEqual(logging.getLoggerClass(), logging.Logger)
2597
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002598class LogRecordTest(BaseTest):
2599 def test_str_rep(self):
2600 r = logging.makeLogRecord({})
2601 s = str(r)
2602 self.assertTrue(s.startswith('<LogRecord: '))
2603 self.assertTrue(s.endswith('>'))
2604
2605 def test_dict_arg(self):
2606 h = RecordingHandler()
2607 r = logging.getLogger()
2608 r.addHandler(h)
2609 d = {'less' : 'more' }
2610 logging.warning('less is %(less)s', d)
2611 self.assertIs(h.records[0].args, d)
2612 self.assertEqual(h.records[0].message, 'less is more')
2613 r.removeHandler(h)
2614 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002615
2616class BasicConfigTest(unittest.TestCase):
2617
Vinay Sajip95bf5042011-04-20 11:50:56 +01002618 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002619
2620 def setUp(self):
2621 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01002622 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01002623 self.saved_handlers = logging._handlers.copy()
2624 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002625 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002626 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002627 logging.root.handlers = []
2628
2629 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01002630 for h in logging.root.handlers[:]:
2631 logging.root.removeHandler(h)
2632 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002633 super(BasicConfigTest, self).tearDown()
2634
Vinay Sajip3def7e02011-04-20 10:58:06 +01002635 def cleanup(self):
2636 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01002637 logging._handlers.clear()
2638 logging._handlers.update(self.saved_handlers)
2639 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002640 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002641
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002642 def test_no_kwargs(self):
2643 logging.basicConfig()
2644
2645 # handler defaults to a StreamHandler to sys.stderr
2646 self.assertEqual(len(logging.root.handlers), 1)
2647 handler = logging.root.handlers[0]
2648 self.assertIsInstance(handler, logging.StreamHandler)
2649 self.assertEqual(handler.stream, sys.stderr)
2650
2651 formatter = handler.formatter
2652 # format defaults to logging.BASIC_FORMAT
2653 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
2654 # datefmt defaults to None
2655 self.assertIsNone(formatter.datefmt)
2656 # style defaults to %
2657 self.assertIsInstance(formatter._style, logging.PercentStyle)
2658
2659 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01002660 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002661
2662 def test_filename(self):
2663 logging.basicConfig(filename='test.log')
2664
2665 self.assertEqual(len(logging.root.handlers), 1)
2666 handler = logging.root.handlers[0]
2667 self.assertIsInstance(handler, logging.FileHandler)
2668
2669 expected = logging.FileHandler('test.log', 'a')
2670 self.addCleanup(expected.close)
2671 self.assertEqual(handler.stream.mode, expected.stream.mode)
2672 self.assertEqual(handler.stream.name, expected.stream.name)
2673
2674 def test_filemode(self):
2675 logging.basicConfig(filename='test.log', filemode='wb')
2676
2677 handler = logging.root.handlers[0]
2678 expected = logging.FileHandler('test.log', 'wb')
2679 self.addCleanup(expected.close)
2680 self.assertEqual(handler.stream.mode, expected.stream.mode)
2681
2682 def test_stream(self):
2683 stream = io.StringIO()
2684 self.addCleanup(stream.close)
2685 logging.basicConfig(stream=stream)
2686
2687 self.assertEqual(len(logging.root.handlers), 1)
2688 handler = logging.root.handlers[0]
2689 self.assertIsInstance(handler, logging.StreamHandler)
2690 self.assertEqual(handler.stream, stream)
2691
2692 def test_format(self):
2693 logging.basicConfig(format='foo')
2694
2695 formatter = logging.root.handlers[0].formatter
2696 self.assertEqual(formatter._style._fmt, 'foo')
2697
2698 def test_datefmt(self):
2699 logging.basicConfig(datefmt='bar')
2700
2701 formatter = logging.root.handlers[0].formatter
2702 self.assertEqual(formatter.datefmt, 'bar')
2703
2704 def test_style(self):
2705 logging.basicConfig(style='$')
2706
2707 formatter = logging.root.handlers[0].formatter
2708 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
2709
2710 def test_level(self):
2711 old_level = logging.root.level
2712 self.addCleanup(lambda: logging.root.setLevel(old_level))
2713
2714 logging.basicConfig(level=57)
2715 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002716 # Test that second call has no effect
2717 logging.basicConfig(level=58)
2718 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002719
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002720 def test_incompatible(self):
2721 assertRaises = self.assertRaises
2722 handlers = [logging.StreamHandler()]
2723 stream = sys.stderr
2724 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2725 stream=stream)
2726 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2727 handlers=handlers)
2728 assertRaises(ValueError, logging.basicConfig, stream=stream,
2729 handlers=handlers)
2730
2731 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002732 handlers = [
2733 logging.StreamHandler(),
2734 logging.StreamHandler(sys.stdout),
2735 logging.StreamHandler(),
2736 ]
2737 f = logging.Formatter()
2738 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002739 logging.basicConfig(handlers=handlers)
2740 self.assertIs(handlers[0], logging.root.handlers[0])
2741 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002742 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002743 self.assertIsNotNone(handlers[0].formatter)
2744 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002745 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002746 self.assertIs(handlers[0].formatter, handlers[1].formatter)
2747
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002748 def _test_log(self, method, level=None):
2749 # logging.root has no handlers so basicConfig should be called
2750 called = []
2751
2752 old_basic_config = logging.basicConfig
2753 def my_basic_config(*a, **kw):
2754 old_basic_config()
2755 old_level = logging.root.level
2756 logging.root.setLevel(100) # avoid having messages in stderr
2757 self.addCleanup(lambda: logging.root.setLevel(old_level))
2758 called.append((a, kw))
2759
2760 patch(self, logging, 'basicConfig', my_basic_config)
2761
2762 log_method = getattr(logging, method)
2763 if level is not None:
2764 log_method(level, "test me")
2765 else:
2766 log_method("test me")
2767
2768 # basicConfig was called with no arguments
2769 self.assertEqual(called, [((), {})])
2770
2771 def test_log(self):
2772 self._test_log('log', logging.WARNING)
2773
2774 def test_debug(self):
2775 self._test_log('debug')
2776
2777 def test_info(self):
2778 self._test_log('info')
2779
2780 def test_warning(self):
2781 self._test_log('warning')
2782
2783 def test_error(self):
2784 self._test_log('error')
2785
2786 def test_critical(self):
2787 self._test_log('critical')
2788
2789
2790class LoggerAdapterTest(unittest.TestCase):
2791
2792 def setUp(self):
2793 super(LoggerAdapterTest, self).setUp()
2794 old_handler_list = logging._handlerList[:]
2795
2796 self.recording = RecordingHandler()
2797 self.logger = logging.root
2798 self.logger.addHandler(self.recording)
2799 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2800 self.addCleanup(self.recording.close)
2801
2802 def cleanup():
2803 logging._handlerList[:] = old_handler_list
2804
2805 self.addCleanup(cleanup)
2806 self.addCleanup(logging.shutdown)
2807 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
2808
2809 def test_exception(self):
2810 msg = 'testing exception: %r'
2811 exc = None
2812 try:
2813 assert False
2814 except AssertionError as e:
2815 exc = e
2816 self.adapter.exception(msg, self.recording)
2817
2818 self.assertEqual(len(self.recording.records), 1)
2819 record = self.recording.records[0]
2820 self.assertEqual(record.levelno, logging.ERROR)
2821 self.assertEqual(record.msg, msg)
2822 self.assertEqual(record.args, (self.recording,))
2823 self.assertEqual(record.exc_info,
2824 (exc.__class__, exc, exc.__traceback__))
2825
2826 def test_critical(self):
2827 msg = 'critical test! %r'
2828 self.adapter.critical(msg, self.recording)
2829
2830 self.assertEqual(len(self.recording.records), 1)
2831 record = self.recording.records[0]
2832 self.assertEqual(record.levelno, logging.CRITICAL)
2833 self.assertEqual(record.msg, msg)
2834 self.assertEqual(record.args, (self.recording,))
2835
2836 def test_is_enabled_for(self):
2837 old_disable = self.adapter.logger.manager.disable
2838 self.adapter.logger.manager.disable = 33
2839 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
2840 'disable', old_disable))
2841 self.assertFalse(self.adapter.isEnabledFor(32))
2842
2843 def test_has_handlers(self):
2844 self.assertTrue(self.adapter.hasHandlers())
2845
2846 for handler in self.logger.handlers:
2847 self.logger.removeHandler(handler)
2848 assert not self.logger.hasHandlers()
2849
2850 self.assertFalse(self.adapter.hasHandlers())
2851
2852
2853class LoggerTest(BaseTest):
2854
2855 def setUp(self):
2856 super(LoggerTest, self).setUp()
2857 self.recording = RecordingHandler()
2858 self.logger = logging.Logger(name='blah')
2859 self.logger.addHandler(self.recording)
2860 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2861 self.addCleanup(self.recording.close)
2862 self.addCleanup(logging.shutdown)
2863
2864 def test_set_invalid_level(self):
2865 self.assertRaises(TypeError, self.logger.setLevel, object())
2866
2867 def test_exception(self):
2868 msg = 'testing exception: %r'
2869 exc = None
2870 try:
2871 assert False
2872 except AssertionError as e:
2873 exc = e
2874 self.logger.exception(msg, self.recording)
2875
2876 self.assertEqual(len(self.recording.records), 1)
2877 record = self.recording.records[0]
2878 self.assertEqual(record.levelno, logging.ERROR)
2879 self.assertEqual(record.msg, msg)
2880 self.assertEqual(record.args, (self.recording,))
2881 self.assertEqual(record.exc_info,
2882 (exc.__class__, exc, exc.__traceback__))
2883
2884 def test_log_invalid_level_with_raise(self):
2885 old_raise = logging.raiseExceptions
2886 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2887
2888 logging.raiseExceptions = True
2889 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
2890
2891 def test_log_invalid_level_no_raise(self):
2892 old_raise = logging.raiseExceptions
2893 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2894
2895 logging.raiseExceptions = False
2896 self.logger.log('10', 'test message') # no exception happens
2897
2898 def test_find_caller_with_stack_info(self):
2899 called = []
2900 patch(self, logging.traceback, 'print_stack',
2901 lambda f, file: called.append(file.getvalue()))
2902
2903 self.logger.findCaller(stack_info=True)
2904
2905 self.assertEqual(len(called), 1)
2906 self.assertEqual('Stack (most recent call last):\n', called[0])
2907
2908 def test_make_record_with_extra_overwrite(self):
2909 name = 'my record'
2910 level = 13
2911 fn = lno = msg = args = exc_info = func = sinfo = None
2912 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
2913 exc_info, func, sinfo)
2914
2915 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
2916 extra = {key: 'some value'}
2917 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
2918 fn, lno, msg, args, exc_info,
2919 extra=extra, sinfo=sinfo)
2920
2921 def test_make_record_with_extra_no_overwrite(self):
2922 name = 'my record'
2923 level = 13
2924 fn = lno = msg = args = exc_info = func = sinfo = None
2925 extra = {'valid_key': 'some value'}
2926 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
2927 exc_info, extra=extra, sinfo=sinfo)
2928 self.assertIn('valid_key', result.__dict__)
2929
2930 def test_has_handlers(self):
2931 self.assertTrue(self.logger.hasHandlers())
2932
2933 for handler in self.logger.handlers:
2934 self.logger.removeHandler(handler)
2935 assert not self.logger.hasHandlers()
2936
2937 self.assertFalse(self.logger.hasHandlers())
2938
2939 def test_has_handlers_no_propagate(self):
2940 child_logger = logging.getLogger('blah.child')
2941 child_logger.propagate = False
2942 assert child_logger.handlers == []
2943
2944 self.assertFalse(child_logger.hasHandlers())
2945
2946 def test_is_enabled_for(self):
2947 old_disable = self.logger.manager.disable
2948 self.logger.manager.disable = 23
2949 self.addCleanup(lambda: setattr(self.logger.manager,
2950 'disable', old_disable))
2951 self.assertFalse(self.logger.isEnabledFor(22))
2952
2953
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002954class BaseFileTest(BaseTest):
2955 "Base class for handler tests that write log files"
2956
2957 def setUp(self):
2958 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002959 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2960 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002961 self.rmfiles = []
2962
2963 def tearDown(self):
2964 for fn in self.rmfiles:
2965 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002966 if os.path.exists(self.fn):
2967 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002968 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002969
2970 def assertLogFile(self, filename):
2971 "Assert a log file is there and register it for deletion"
2972 self.assertTrue(os.path.exists(filename),
2973 msg="Log file %r does not exist")
2974 self.rmfiles.append(filename)
2975
2976
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002977class FileHandlerTest(BaseFileTest):
2978 def test_delay(self):
2979 os.unlink(self.fn)
2980 fh = logging.FileHandler(self.fn, delay=True)
2981 self.assertIsNone(fh.stream)
2982 self.assertFalse(os.path.exists(self.fn))
2983 fh.handle(logging.makeLogRecord({}))
2984 self.assertIsNotNone(fh.stream)
2985 self.assertTrue(os.path.exists(self.fn))
2986 fh.close()
2987
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002988class RotatingFileHandlerTest(BaseFileTest):
2989 def next_rec(self):
2990 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2991 self.next_message(), None, None, None)
2992
2993 def test_should_not_rollover(self):
2994 # If maxbytes is zero rollover never occurs
2995 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2996 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002997 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002998
2999 def test_should_rollover(self):
3000 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3001 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003002 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003003
3004 def test_file_created(self):
3005 # checks that the file is created and assumes it was created
3006 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003007 rh = logging.handlers.RotatingFileHandler(self.fn)
3008 rh.emit(self.next_rec())
3009 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003010 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003011
3012 def test_rollover_filenames(self):
3013 rh = logging.handlers.RotatingFileHandler(
3014 self.fn, backupCount=2, maxBytes=1)
3015 rh.emit(self.next_rec())
3016 self.assertLogFile(self.fn)
3017 rh.emit(self.next_rec())
3018 self.assertLogFile(self.fn + ".1")
3019 rh.emit(self.next_rec())
3020 self.assertLogFile(self.fn + ".2")
3021 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003022 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003023
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003024class TimedRotatingFileHandlerTest(BaseFileTest):
3025 # test methods added below
3026 pass
3027
3028def secs(**kw):
3029 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3030
3031for when, exp in (('S', 1),
3032 ('M', 60),
3033 ('H', 60 * 60),
3034 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003035 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003036 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003037 ('W0', secs(days=4, hours=24)),
3038 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003039 def test_compute_rollover(self, when=when, exp=exp):
3040 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003041 self.fn, when=when, interval=1, backupCount=0, utc=True)
3042 currentTime = 0.0
3043 actual = rh.computeRollover(currentTime)
3044 if exp != actual:
3045 # Failures occur on some systems for MIDNIGHT and W0.
3046 # Print detailed calculation for MIDNIGHT so we can try to see
3047 # what's going on
3048 import time
3049 if when == 'MIDNIGHT':
3050 try:
3051 if rh.utc:
3052 t = time.gmtime(currentTime)
3053 else:
3054 t = time.localtime(currentTime)
3055 currentHour = t[3]
3056 currentMinute = t[4]
3057 currentSecond = t[5]
3058 # r is the number of seconds left between now and midnight
3059 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3060 currentMinute) * 60 +
3061 currentSecond)
3062 result = currentTime + r
3063 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3064 print('currentHour: %s' % currentHour, file=sys.stderr)
3065 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3066 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3067 print('r: %s' % r, file=sys.stderr)
3068 print('result: %s' % result, file=sys.stderr)
3069 except Exception:
3070 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3071 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003072 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003073 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3074
Christian Heimes180510d2008-03-03 19:15:45 +00003075# Set the locale to the platform-dependent default. I have no idea
3076# why the test does this, but in any case we save the current locale
3077# first and restore it at the end.
3078@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003079def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003080 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003081 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003082 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003083 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajip985ef872011-04-26 19:34:04 +01003084 FormatterTest, BufferingFormatterTest, StreamHandlerTest,
Vinay Sajip61561522010-12-03 11:50:38 +00003085 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003086 ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
3087 LoggerAdapterTest, LoggerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003088 FileHandlerTest, RotatingFileHandlerTest,
3089 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003090 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003091 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003092
Christian Heimes180510d2008-03-03 19:15:45 +00003093if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003094 test_main()