blob: 24a6149b840a35491b8499050754de9c29cf4b0a [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
Christian Heimes180510d2008-03-03 19:15:45 +000039import struct
40import sys
41import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010042from test.support import captured_stdout, run_with_locale, run_unittest, patch
Vinay Sajipe723e962011-04-15 22:27:17 +010043from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000044import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010045import time
Christian Heimes180510d2008-03-03 19:15:45 +000046import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000047import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000048import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000049try:
50 import threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010051 # The following imports are needed only for tests which
52 import asynchat
53 import asyncore
54 import errno
55 from http.server import HTTPServer, BaseHTTPRequestHandler
56 import smtpd
57 from urllib.parse import urlparse, parse_qs
58 from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
59 ThreadingTCPServer, StreamRequestHandler)
Victor Stinner45df8202010-04-28 22:31:17 +000060except ImportError:
61 threading = None
Vinay Sajip60ccd822011-05-09 17:32:09 +010062try:
63 import win32evtlog
64except ImportError:
65 win32evtlog = None
66try:
67 import win32evtlogutil
68except ImportError:
69 win32evtlogutil = None
70 win32evtlog = None
Christian Heimes18c66892008-02-17 13:31:39 +000071
72
Christian Heimes180510d2008-03-03 19:15:45 +000073class BaseTest(unittest.TestCase):
74
75 """Base class for logging tests."""
76
77 log_format = "%(name)s -> %(levelname)s: %(message)s"
78 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
79 message_num = 0
80
81 def setUp(self):
82 """Setup the default logging stream to an internal StringIO instance,
83 so that we can examine log output as we want."""
84 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000085 logging._acquireLock()
86 try:
Christian Heimes180510d2008-03-03 19:15:45 +000087 self.saved_handlers = logging._handlers.copy()
88 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000089 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000090 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000091 self.logger_states = logger_states = {}
92 for name in saved_loggers:
93 logger_states[name] = getattr(saved_loggers[name],
94 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000095 finally:
96 logging._releaseLock()
97
Benjamin Peterson22005fc2010-04-11 16:25:06 +000098 # Set two unused loggers: one non-ASCII and one Unicode.
99 # This is to test correct operation when sorting existing
100 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +0000101 self.logger1 = logging.getLogger("\xab\xd7\xbb")
102 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000103
Christian Heimes180510d2008-03-03 19:15:45 +0000104 self.root_logger = logging.getLogger("")
105 self.original_logging_level = self.root_logger.getEffectiveLevel()
106
107 self.stream = io.StringIO()
108 self.root_logger.setLevel(logging.DEBUG)
109 self.root_hdlr = logging.StreamHandler(self.stream)
110 self.root_formatter = logging.Formatter(self.log_format)
111 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000112 if self.logger1.hasHandlers():
113 hlist = self.logger1.handlers + self.root_logger.handlers
114 raise AssertionError('Unexpected handlers: %s' % hlist)
115 if self.logger2.hasHandlers():
116 hlist = self.logger2.handlers + self.root_logger.handlers
117 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000118 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000119 self.assertTrue(self.logger1.hasHandlers())
120 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000121
122 def tearDown(self):
123 """Remove our logging stream, and restore the original logging
124 level."""
125 self.stream.close()
126 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000127 while self.root_logger.handlers:
128 h = self.root_logger.handlers[0]
129 self.root_logger.removeHandler(h)
130 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000131 self.root_logger.setLevel(self.original_logging_level)
132 logging._acquireLock()
133 try:
134 logging._levelNames.clear()
135 logging._levelNames.update(self.saved_level_names)
136 logging._handlers.clear()
137 logging._handlers.update(self.saved_handlers)
138 logging._handlerList[:] = self.saved_handler_list
139 loggerDict = logging.getLogger().manager.loggerDict
140 loggerDict.clear()
141 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000142 logger_states = self.logger_states
143 for name in self.logger_states:
144 if logger_states[name] is not None:
145 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000146 finally:
147 logging._releaseLock()
148
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000149 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000150 """Match the collected log lines against the regular expression
151 self.expected_log_pat, and compare the extracted group values to
152 the expected_values list of tuples."""
153 stream = stream or self.stream
154 pat = re.compile(self.expected_log_pat)
155 try:
156 stream.reset()
157 actual_lines = stream.readlines()
158 except AttributeError:
159 # StringIO.StringIO lacks a reset() method.
160 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000161 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000162 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000163 for actual, expected in zip(actual_lines, expected_values):
164 match = pat.search(actual)
165 if not match:
166 self.fail("Log line does not match expected pattern:\n" +
167 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000168 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000169 s = stream.read()
170 if s:
171 self.fail("Remaining output at end of log stream:\n" + s)
172
173 def next_message(self):
174 """Generate a message consisting solely of an auto-incrementing
175 integer."""
176 self.message_num += 1
177 return "%d" % self.message_num
178
179
180class BuiltinLevelsTest(BaseTest):
181 """Test builtin levels and their inheritance."""
182
183 def test_flat(self):
184 #Logging levels in a flat logger namespace.
185 m = self.next_message
186
187 ERR = logging.getLogger("ERR")
188 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000189 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000190 INF.setLevel(logging.INFO)
191 DEB = logging.getLogger("DEB")
192 DEB.setLevel(logging.DEBUG)
193
194 # These should log.
195 ERR.log(logging.CRITICAL, m())
196 ERR.error(m())
197
198 INF.log(logging.CRITICAL, m())
199 INF.error(m())
200 INF.warn(m())
201 INF.info(m())
202
203 DEB.log(logging.CRITICAL, m())
204 DEB.error(m())
205 DEB.warn (m())
206 DEB.info (m())
207 DEB.debug(m())
208
209 # These should not log.
210 ERR.warn(m())
211 ERR.info(m())
212 ERR.debug(m())
213
214 INF.debug(m())
215
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000216 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000217 ('ERR', 'CRITICAL', '1'),
218 ('ERR', 'ERROR', '2'),
219 ('INF', 'CRITICAL', '3'),
220 ('INF', 'ERROR', '4'),
221 ('INF', 'WARNING', '5'),
222 ('INF', 'INFO', '6'),
223 ('DEB', 'CRITICAL', '7'),
224 ('DEB', 'ERROR', '8'),
225 ('DEB', 'WARNING', '9'),
226 ('DEB', 'INFO', '10'),
227 ('DEB', 'DEBUG', '11'),
228 ])
229
230 def test_nested_explicit(self):
231 # Logging levels in a nested namespace, all explicitly set.
232 m = self.next_message
233
234 INF = logging.getLogger("INF")
235 INF.setLevel(logging.INFO)
236 INF_ERR = logging.getLogger("INF.ERR")
237 INF_ERR.setLevel(logging.ERROR)
238
239 # These should log.
240 INF_ERR.log(logging.CRITICAL, m())
241 INF_ERR.error(m())
242
243 # These should not log.
244 INF_ERR.warn(m())
245 INF_ERR.info(m())
246 INF_ERR.debug(m())
247
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000248 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000249 ('INF.ERR', 'CRITICAL', '1'),
250 ('INF.ERR', 'ERROR', '2'),
251 ])
252
253 def test_nested_inherited(self):
254 #Logging levels in a nested namespace, inherited from parent loggers.
255 m = self.next_message
256
257 INF = logging.getLogger("INF")
258 INF.setLevel(logging.INFO)
259 INF_ERR = logging.getLogger("INF.ERR")
260 INF_ERR.setLevel(logging.ERROR)
261 INF_UNDEF = logging.getLogger("INF.UNDEF")
262 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
263 UNDEF = logging.getLogger("UNDEF")
264
265 # These should log.
266 INF_UNDEF.log(logging.CRITICAL, m())
267 INF_UNDEF.error(m())
268 INF_UNDEF.warn(m())
269 INF_UNDEF.info(m())
270 INF_ERR_UNDEF.log(logging.CRITICAL, m())
271 INF_ERR_UNDEF.error(m())
272
273 # These should not log.
274 INF_UNDEF.debug(m())
275 INF_ERR_UNDEF.warn(m())
276 INF_ERR_UNDEF.info(m())
277 INF_ERR_UNDEF.debug(m())
278
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000279 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000280 ('INF.UNDEF', 'CRITICAL', '1'),
281 ('INF.UNDEF', 'ERROR', '2'),
282 ('INF.UNDEF', 'WARNING', '3'),
283 ('INF.UNDEF', 'INFO', '4'),
284 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
285 ('INF.ERR.UNDEF', 'ERROR', '6'),
286 ])
287
288 def test_nested_with_virtual_parent(self):
289 # Logging levels when some parent does not exist yet.
290 m = self.next_message
291
292 INF = logging.getLogger("INF")
293 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
294 CHILD = logging.getLogger("INF.BADPARENT")
295 INF.setLevel(logging.INFO)
296
297 # These should log.
298 GRANDCHILD.log(logging.FATAL, m())
299 GRANDCHILD.info(m())
300 CHILD.log(logging.FATAL, m())
301 CHILD.info(m())
302
303 # These should not log.
304 GRANDCHILD.debug(m())
305 CHILD.debug(m())
306
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000307 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000308 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
309 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
310 ('INF.BADPARENT', 'CRITICAL', '3'),
311 ('INF.BADPARENT', 'INFO', '4'),
312 ])
313
314
315class BasicFilterTest(BaseTest):
316
317 """Test the bundled Filter class."""
318
319 def test_filter(self):
320 # Only messages satisfying the specified criteria pass through the
321 # filter.
322 filter_ = logging.Filter("spam.eggs")
323 handler = self.root_logger.handlers[0]
324 try:
325 handler.addFilter(filter_)
326 spam = logging.getLogger("spam")
327 spam_eggs = logging.getLogger("spam.eggs")
328 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
329 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
330
331 spam.info(self.next_message())
332 spam_eggs.info(self.next_message()) # Good.
333 spam_eggs_fish.info(self.next_message()) # Good.
334 spam_bakedbeans.info(self.next_message())
335
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000336 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000337 ('spam.eggs', 'INFO', '2'),
338 ('spam.eggs.fish', 'INFO', '3'),
339 ])
340 finally:
341 handler.removeFilter(filter_)
342
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000343 def test_callable_filter(self):
344 # Only messages satisfying the specified criteria pass through the
345 # filter.
346
347 def filterfunc(record):
348 parts = record.name.split('.')
349 prefix = '.'.join(parts[:2])
350 return prefix == 'spam.eggs'
351
352 handler = self.root_logger.handlers[0]
353 try:
354 handler.addFilter(filterfunc)
355 spam = logging.getLogger("spam")
356 spam_eggs = logging.getLogger("spam.eggs")
357 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
358 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
359
360 spam.info(self.next_message())
361 spam_eggs.info(self.next_message()) # Good.
362 spam_eggs_fish.info(self.next_message()) # Good.
363 spam_bakedbeans.info(self.next_message())
364
365 self.assert_log_lines([
366 ('spam.eggs', 'INFO', '2'),
367 ('spam.eggs.fish', 'INFO', '3'),
368 ])
369 finally:
370 handler.removeFilter(filterfunc)
371
Vinay Sajip985ef872011-04-26 19:34:04 +0100372 def test_empty_filter(self):
373 f = logging.Filter()
374 r = logging.makeLogRecord({'name': 'spam.eggs'})
375 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000376
377#
378# First, we define our levels. There can be as many as you want - the only
379# limitations are that they should be integers, the lowest should be > 0 and
380# larger values mean less information being logged. If you need specific
381# level values which do not fit into these limitations, you can use a
382# mapping dictionary to convert between your application levels and the
383# logging system.
384#
385SILENT = 120
386TACITURN = 119
387TERSE = 118
388EFFUSIVE = 117
389SOCIABLE = 116
390VERBOSE = 115
391TALKATIVE = 114
392GARRULOUS = 113
393CHATTERBOX = 112
394BORING = 111
395
396LEVEL_RANGE = range(BORING, SILENT + 1)
397
398#
399# Next, we define names for our levels. You don't need to do this - in which
400# case the system will use "Level n" to denote the text for the level.
401#
402my_logging_levels = {
403 SILENT : 'Silent',
404 TACITURN : 'Taciturn',
405 TERSE : 'Terse',
406 EFFUSIVE : 'Effusive',
407 SOCIABLE : 'Sociable',
408 VERBOSE : 'Verbose',
409 TALKATIVE : 'Talkative',
410 GARRULOUS : 'Garrulous',
411 CHATTERBOX : 'Chatterbox',
412 BORING : 'Boring',
413}
414
415class GarrulousFilter(logging.Filter):
416
417 """A filter which blocks garrulous messages."""
418
419 def filter(self, record):
420 return record.levelno != GARRULOUS
421
422class VerySpecificFilter(logging.Filter):
423
424 """A filter which blocks sociable and taciturn messages."""
425
426 def filter(self, record):
427 return record.levelno not in [SOCIABLE, TACITURN]
428
429
430class CustomLevelsAndFiltersTest(BaseTest):
431
432 """Test various filtering possibilities with custom logging levels."""
433
434 # Skip the logger name group.
435 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
436
437 def setUp(self):
438 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000439 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000440 logging.addLevelName(k, v)
441
442 def log_at_all_levels(self, logger):
443 for lvl in LEVEL_RANGE:
444 logger.log(lvl, self.next_message())
445
446 def test_logger_filter(self):
447 # Filter at logger level.
448 self.root_logger.setLevel(VERBOSE)
449 # Levels >= 'Verbose' are good.
450 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000451 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000452 ('Verbose', '5'),
453 ('Sociable', '6'),
454 ('Effusive', '7'),
455 ('Terse', '8'),
456 ('Taciturn', '9'),
457 ('Silent', '10'),
458 ])
459
460 def test_handler_filter(self):
461 # Filter at handler level.
462 self.root_logger.handlers[0].setLevel(SOCIABLE)
463 try:
464 # Levels >= 'Sociable' are good.
465 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000466 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000467 ('Sociable', '6'),
468 ('Effusive', '7'),
469 ('Terse', '8'),
470 ('Taciturn', '9'),
471 ('Silent', '10'),
472 ])
473 finally:
474 self.root_logger.handlers[0].setLevel(logging.NOTSET)
475
476 def test_specific_filters(self):
477 # Set a specific filter object on the handler, and then add another
478 # filter object on the logger itself.
479 handler = self.root_logger.handlers[0]
480 specific_filter = None
481 garr = GarrulousFilter()
482 handler.addFilter(garr)
483 try:
484 self.log_at_all_levels(self.root_logger)
485 first_lines = [
486 # Notice how 'Garrulous' is missing
487 ('Boring', '1'),
488 ('Chatterbox', '2'),
489 ('Talkative', '4'),
490 ('Verbose', '5'),
491 ('Sociable', '6'),
492 ('Effusive', '7'),
493 ('Terse', '8'),
494 ('Taciturn', '9'),
495 ('Silent', '10'),
496 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000497 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000498
499 specific_filter = VerySpecificFilter()
500 self.root_logger.addFilter(specific_filter)
501 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000502 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000503 # Not only 'Garrulous' is still missing, but also 'Sociable'
504 # and 'Taciturn'
505 ('Boring', '11'),
506 ('Chatterbox', '12'),
507 ('Talkative', '14'),
508 ('Verbose', '15'),
509 ('Effusive', '17'),
510 ('Terse', '18'),
511 ('Silent', '20'),
512 ])
513 finally:
514 if specific_filter:
515 self.root_logger.removeFilter(specific_filter)
516 handler.removeFilter(garr)
517
518
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100519class HandlerTest(BaseTest):
520 def test_name(self):
521 h = logging.Handler()
522 h.name = 'generic'
523 self.assertEqual(h.name, 'generic')
524 h.name = 'anothergeneric'
525 self.assertEqual(h.name, 'anothergeneric')
526 self.assertRaises(NotImplementedError, h.emit, None)
527
Vinay Sajip5a35b062011-04-27 11:31:14 +0100528 def test_builtin_handlers(self):
529 # We can't actually *use* too many handlers in the tests,
530 # but we can try instantiating them with various options
531 if sys.platform in ('linux2', 'darwin'):
532 for existing in (True, False):
533 fd, fn = tempfile.mkstemp()
534 os.close(fd)
535 if not existing:
536 os.unlink(fn)
537 h = logging.handlers.WatchedFileHandler(fn, delay=True)
538 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100539 dev, ino = h.dev, h.ino
540 self.assertNotEqual(dev, -1)
541 self.assertNotEqual(ino, -1)
542 r = logging.makeLogRecord({'msg': 'Test'})
543 h.handle(r)
544 # Now remove the file.
545 os.unlink(fn)
546 self.assertFalse(os.path.exists(fn))
547 # The next call should recreate the file.
548 h.handle(r)
549 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100550 else:
551 self.assertEqual(h.dev, -1)
552 self.assertEqual(h.ino, -1)
553 h.close()
554 if existing:
555 os.unlink(fn)
556 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100557 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100558 else:
559 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100560 try:
561 h = logging.handlers.SysLogHandler(sockname)
562 self.assertEqual(h.facility, h.LOG_USER)
563 self.assertTrue(h.unixsocket)
564 h.close()
565 except socket.error: # syslogd might not be available
566 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100567 for method in ('GET', 'POST', 'PUT'):
568 if method == 'PUT':
569 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
570 'localhost', '/log', method)
571 else:
572 h = logging.handlers.HTTPHandler('localhost', '/log', method)
573 h.close()
574 h = logging.handlers.BufferingHandler(0)
575 r = logging.makeLogRecord({})
576 self.assertTrue(h.shouldFlush(r))
577 h.close()
578 h = logging.handlers.BufferingHandler(1)
579 self.assertFalse(h.shouldFlush(r))
580 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100581
582class BadStream(object):
583 def write(self, data):
584 raise RuntimeError('deliberate mistake')
585
586class TestStreamHandler(logging.StreamHandler):
587 def handleError(self, record):
588 self.error_record = record
589
590class StreamHandlerTest(BaseTest):
591 def test_error_handling(self):
592 h = TestStreamHandler(BadStream())
593 r = logging.makeLogRecord({})
594 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100595 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100596 try:
597 h.handle(r)
598 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100599 h = logging.StreamHandler(BadStream())
600 sys.stderr = sio = io.StringIO()
601 h.handle(r)
602 self.assertTrue('\nRuntimeError: '
603 'deliberate mistake\n' in sio.getvalue())
604 logging.raiseExceptions = False
605 sys.stderr = sio = io.StringIO()
606 h.handle(r)
607 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100608 finally:
609 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100610 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100611
Vinay Sajip7367d082011-05-02 13:17:27 +0100612# -- The following section could be moved into a server_helper.py module
613# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100614
Vinay Sajipce7c9782011-05-17 07:15:53 +0100615if threading:
616 class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100617 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100618 This derived class has had to be created because smtpd does not
619 support use of custom channel maps, although they are allowed by
620 asyncore's design. Issue #11959 has been raised to address this,
621 and if resolved satisfactorily, some of this code can be removed.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100622 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100623 def __init__(self, server, conn, addr, sockmap):
624 asynchat.async_chat.__init__(self, conn, sockmap)
625 self.smtp_server = server
626 self.conn = conn
627 self.addr = addr
628 self.received_lines = []
629 self.smtp_state = self.COMMAND
630 self.seen_greeting = ''
631 self.mailfrom = None
632 self.rcpttos = []
633 self.received_data = ''
634 self.fqdn = socket.getfqdn()
635 self.num_bytes = 0
636 try:
637 self.peer = conn.getpeername()
638 except socket.error as err:
639 # a race condition may occur if the other end is closing
640 # before we can get the peername
641 self.close()
642 if err.args[0] != errno.ENOTCONN:
643 raise
644 return
645 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
646 self.set_terminator(b'\r\n')
Vinay Sajipa463d252011-04-30 21:52:48 +0100647
Vinay Sajip314b92b2011-05-02 14:31:16 +0100648
Vinay Sajipce7c9782011-05-17 07:15:53 +0100649 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100650 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100651 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100652
Vinay Sajipce7c9782011-05-17 07:15:53 +0100653 :param addr: A (host, port) tuple which the server listens on.
654 You can specify a port value of zero: the server's
655 *port* attribute will hold the actual port number
656 used, which can be used in client connections.
657 :param handler: A callable which will be called to process
658 incoming messages. The handler will be passed
659 the client address tuple, who the message is from,
660 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100661 :param poll_interval: The interval, in seconds, used in the underlying
662 :func:`select` or :func:`poll` call by
663 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100664 :param sockmap: A dictionary which will be used to hold
665 :class:`asyncore.dispatcher` instances used by
666 :func:`asyncore.loop`. This avoids changing the
667 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100668 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100669 channel_class = TestSMTPChannel
670
671 def __init__(self, addr, handler, poll_interval, sockmap):
672 self._localaddr = addr
673 self._remoteaddr = None
674 self.sockmap = sockmap
675 asyncore.dispatcher.__init__(self, map=sockmap)
676 try:
677 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
678 sock.setblocking(0)
679 self.set_socket(sock, map=sockmap)
680 # try to re-use a server port if possible
681 self.set_reuse_addr()
682 self.bind(addr)
683 self.port = sock.getsockname()[1]
684 self.listen(5)
685 except:
686 self.close()
Vinay Sajipe73afad2011-05-10 07:48:28 +0100687 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +0100688 self._handler = handler
689 self._thread = None
690 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100691
Vinay Sajipce7c9782011-05-17 07:15:53 +0100692 def handle_accepted(self, conn, addr):
693 """
694 Redefined only because the base class does not pass in a
695 map, forcing use of a global in :mod:`asyncore`.
696 """
697 channel = self.channel_class(self, conn, addr, self.sockmap)
Vinay Sajip314b92b2011-05-02 14:31:16 +0100698
Vinay Sajipce7c9782011-05-17 07:15:53 +0100699 def process_message(self, peer, mailfrom, rcpttos, data):
700 """
701 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100702
Vinay Sajipce7c9782011-05-17 07:15:53 +0100703 Typically, this will be a test case method.
704 :param peer: The client (host, port) tuple.
705 :param mailfrom: The address of the sender.
706 :param rcpttos: The addresses of the recipients.
707 :param data: The message.
708 """
709 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100710
Vinay Sajipce7c9782011-05-17 07:15:53 +0100711 def start(self):
712 """
713 Start the server running on a separate daemon thread.
714 """
715 self._thread = t = threading.Thread(target=self.serve_forever,
716 args=(self.poll_interval,))
717 t.setDaemon(True)
718 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100719
Vinay Sajipce7c9782011-05-17 07:15:53 +0100720 def serve_forever(self, poll_interval):
721 """
722 Run the :mod:`asyncore` loop until normal termination
723 conditions arise.
724 :param poll_interval: The interval, in seconds, used in the underlying
725 :func:`select` or :func:`poll` call by
726 :func:`asyncore.loop`.
727 """
728 try:
729 asyncore.loop(poll_interval, map=self.sockmap)
730 except select.error:
731 # On FreeBSD 8, closing the server repeatably
732 # raises this error. We swallow it if the
733 # server has been closed.
734 if self.connected or self.accepting:
735 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100736
Vinay Sajipce7c9782011-05-17 07:15:53 +0100737 def stop(self, timeout=None):
738 """
739 Stop the thread by closing the server instance.
740 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100741
Vinay Sajipce7c9782011-05-17 07:15:53 +0100742 :param timeout: How long to wait for the server thread
743 to terminate.
744 """
745 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100746 self._thread.join(timeout)
747 self._thread = None
Vinay Sajip7367d082011-05-02 13:17:27 +0100748
Vinay Sajipce7c9782011-05-17 07:15:53 +0100749 class ControlMixin(object):
750 """
751 This mixin is used to start a server on a separate thread, and
752 shut it down programmatically. Request handling is simplified - instead
753 of needing to derive a suitable RequestHandler subclass, you just
754 provide a callable which will be passed each received request to be
755 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100756
Vinay Sajipce7c9782011-05-17 07:15:53 +0100757 :param handler: A handler callable which will be called with a
758 single parameter - the request - in order to
759 process the request. This handler is called on the
760 server thread, effectively meaning that requests are
761 processed serially. While not quite Web scale ;-),
762 this should be fine for testing applications.
763 :param poll_interval: The polling interval in seconds.
764 """
765 def __init__(self, handler, poll_interval):
766 self._thread = None
767 self.poll_interval = poll_interval
768 self._handler = handler
769 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100770
Vinay Sajipce7c9782011-05-17 07:15:53 +0100771 def start(self):
772 """
773 Create a daemon thread to run the server, and start it.
774 """
775 self._thread = t = threading.Thread(target=self.serve_forever,
776 args=(self.poll_interval,))
777 t.setDaemon(True)
778 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100779
Vinay Sajipce7c9782011-05-17 07:15:53 +0100780 def serve_forever(self, poll_interval):
781 """
782 Run the server. Set the ready flag before entering the
783 service loop.
784 """
785 self.ready.set()
786 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100787
Vinay Sajipce7c9782011-05-17 07:15:53 +0100788 def stop(self, timeout=None):
789 """
790 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100791
Vinay Sajipce7c9782011-05-17 07:15:53 +0100792 :param timeout: How long to wait for the server thread
793 to terminate.
794 """
795 self.shutdown()
796 if self._thread is not None:
797 self._thread.join(timeout)
798 self._thread = None
799 self.server_close()
800 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100801
Vinay Sajipce7c9782011-05-17 07:15:53 +0100802 class TestHTTPServer(ControlMixin, HTTPServer):
803 """
804 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100805
Vinay Sajipce7c9782011-05-17 07:15:53 +0100806 :param addr: A tuple with the IP address and port to listen on.
807 :param handler: A handler callable which will be called with a
808 single parameter - the request - in order to
809 process the request.
810 :param poll_interval: The polling interval in seconds.
811 :param log: Pass ``True`` to enable log messages.
812 """
813 def __init__(self, addr, handler, poll_interval=0.5, log=False):
814 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
815 def __getattr__(self, name, default=None):
816 if name.startswith('do_'):
817 return self.process_request
818 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100819
Vinay Sajipce7c9782011-05-17 07:15:53 +0100820 def process_request(self):
821 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100822
Vinay Sajipce7c9782011-05-17 07:15:53 +0100823 def log_message(self, format, *args):
824 if log:
825 super(DelegatingHTTPRequestHandler,
826 self).log_message(format, *args)
827 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
828 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100829
Vinay Sajipce7c9782011-05-17 07:15:53 +0100830 class TestTCPServer(ControlMixin, ThreadingTCPServer):
831 """
832 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100833
Vinay Sajipce7c9782011-05-17 07:15:53 +0100834 :param addr: A tuple with the IP address and port to listen on.
835 :param handler: A handler callable which will be called with a single
836 parameter - the request - in order to process the request.
837 :param poll_interval: The polling interval in seconds.
838 :bind_and_activate: If True (the default), binds the server and starts it
839 listening. If False, you need to call
840 :meth:`server_bind` and :meth:`server_activate` at
841 some later time before calling :meth:`start`, so that
842 the server will set up the socket and listen on it.
843 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100844
Vinay Sajipce7c9782011-05-17 07:15:53 +0100845 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100846
Vinay Sajipce7c9782011-05-17 07:15:53 +0100847 def __init__(self, addr, handler, poll_interval=0.5,
848 bind_and_activate=True):
849 class DelegatingTCPRequestHandler(StreamRequestHandler):
850
851 def handle(self):
852 self.server._handler(self)
853 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
854 bind_and_activate)
855 ControlMixin.__init__(self, handler, poll_interval)
856
857 def server_bind(self):
858 super(TestTCPServer, self).server_bind()
859 self.port = self.socket.getsockname()[1]
860
861 class TestUDPServer(ControlMixin, ThreadingUDPServer):
862 """
863 A UDP server which is controllable using :class:`ControlMixin`.
864
865 :param addr: A tuple with the IP address and port to listen on.
866 :param handler: A handler callable which will be called with a
867 single parameter - the request - in order to
868 process the request.
869 :param poll_interval: The polling interval for shutdown requests,
870 in seconds.
871 :bind_and_activate: If True (the default), binds the server and
872 starts it listening. If False, you need to
873 call :meth:`server_bind` and
874 :meth:`server_activate` at some later time
875 before calling :meth:`start`, so that the server will
876 set up the socket and listen on it.
877 """
878 def __init__(self, addr, handler, poll_interval=0.5, bind_and_activate=True):
879 class DelegatingUDPRequestHandler(DatagramRequestHandler):
880
881 def handle(self):
882 self.server._handler(self)
883 ThreadingUDPServer.__init__(self, addr, DelegatingUDPRequestHandler,
884 bind_and_activate)
885 ControlMixin.__init__(self, handler, poll_interval)
886
887 def server_bind(self):
888 super(TestUDPServer, self).server_bind()
889 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100890
891
892# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100893
Vinay Sajipce7c9782011-05-17 07:15:53 +0100894@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100895class SMTPHandlerTest(BaseTest):
896 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100897 sockmap = {}
898 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
899 sockmap)
900 server.start()
901 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100902 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
903 self.assertEqual(h.toaddrs, ['you'])
904 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100905 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100906 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100907 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100908 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100909 server.stop()
910 self.assertEqual(len(self.messages), 1)
911 peer, mailfrom, rcpttos, data = self.messages[0]
912 self.assertEqual(mailfrom, 'me')
913 self.assertEqual(rcpttos, ['you'])
914 self.assertTrue('\nSubject: Log\n' in data)
915 self.assertTrue(data.endswith('\n\nHello'))
916 h.close()
917
918 def process_message(self, *args):
919 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100920 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100921
Christian Heimes180510d2008-03-03 19:15:45 +0000922class MemoryHandlerTest(BaseTest):
923
924 """Tests for the MemoryHandler."""
925
926 # Do not bother with a logger name group.
927 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
928
929 def setUp(self):
930 BaseTest.setUp(self)
931 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
932 self.root_hdlr)
933 self.mem_logger = logging.getLogger('mem')
934 self.mem_logger.propagate = 0
935 self.mem_logger.addHandler(self.mem_hdlr)
936
937 def tearDown(self):
938 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000939 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000940
941 def test_flush(self):
942 # The memory handler flushes to its target handler based on specific
943 # criteria (message count and message level).
944 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000945 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000946 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000947 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000948 # This will flush because the level is >= logging.WARNING
949 self.mem_logger.warn(self.next_message())
950 lines = [
951 ('DEBUG', '1'),
952 ('INFO', '2'),
953 ('WARNING', '3'),
954 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000955 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000956 for n in (4, 14):
957 for i in range(9):
958 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000959 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000960 # This will flush because it's the 10th message since the last
961 # flush.
962 self.mem_logger.debug(self.next_message())
963 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000964 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000965
966 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000967 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000968
969
970class ExceptionFormatter(logging.Formatter):
971 """A special exception formatter."""
972 def formatException(self, ei):
973 return "Got a [%s]" % ei[0].__name__
974
975
976class ConfigFileTest(BaseTest):
977
978 """Reading logging config from a .ini-style config file."""
979
980 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
981
982 # config0 is a standard configuration.
983 config0 = """
984 [loggers]
985 keys=root
986
987 [handlers]
988 keys=hand1
989
990 [formatters]
991 keys=form1
992
993 [logger_root]
994 level=WARNING
995 handlers=hand1
996
997 [handler_hand1]
998 class=StreamHandler
999 level=NOTSET
1000 formatter=form1
1001 args=(sys.stdout,)
1002
1003 [formatter_form1]
1004 format=%(levelname)s ++ %(message)s
1005 datefmt=
1006 """
1007
1008 # config1 adds a little to the standard configuration.
1009 config1 = """
1010 [loggers]
1011 keys=root,parser
1012
1013 [handlers]
1014 keys=hand1
1015
1016 [formatters]
1017 keys=form1
1018
1019 [logger_root]
1020 level=WARNING
1021 handlers=
1022
1023 [logger_parser]
1024 level=DEBUG
1025 handlers=hand1
1026 propagate=1
1027 qualname=compiler.parser
1028
1029 [handler_hand1]
1030 class=StreamHandler
1031 level=NOTSET
1032 formatter=form1
1033 args=(sys.stdout,)
1034
1035 [formatter_form1]
1036 format=%(levelname)s ++ %(message)s
1037 datefmt=
1038 """
1039
Vinay Sajip3f84b072011-03-07 17:49:33 +00001040 # config1a moves the handler to the root.
1041 config1a = """
1042 [loggers]
1043 keys=root,parser
1044
1045 [handlers]
1046 keys=hand1
1047
1048 [formatters]
1049 keys=form1
1050
1051 [logger_root]
1052 level=WARNING
1053 handlers=hand1
1054
1055 [logger_parser]
1056 level=DEBUG
1057 handlers=
1058 propagate=1
1059 qualname=compiler.parser
1060
1061 [handler_hand1]
1062 class=StreamHandler
1063 level=NOTSET
1064 formatter=form1
1065 args=(sys.stdout,)
1066
1067 [formatter_form1]
1068 format=%(levelname)s ++ %(message)s
1069 datefmt=
1070 """
1071
Christian Heimes180510d2008-03-03 19:15:45 +00001072 # config2 has a subtle configuration error that should be reported
1073 config2 = config1.replace("sys.stdout", "sys.stbout")
1074
1075 # config3 has a less subtle configuration error
1076 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1077
1078 # config4 specifies a custom formatter class to be loaded
1079 config4 = """
1080 [loggers]
1081 keys=root
1082
1083 [handlers]
1084 keys=hand1
1085
1086 [formatters]
1087 keys=form1
1088
1089 [logger_root]
1090 level=NOTSET
1091 handlers=hand1
1092
1093 [handler_hand1]
1094 class=StreamHandler
1095 level=NOTSET
1096 formatter=form1
1097 args=(sys.stdout,)
1098
1099 [formatter_form1]
1100 class=""" + __name__ + """.ExceptionFormatter
1101 format=%(levelname)s:%(name)s:%(message)s
1102 datefmt=
1103 """
1104
Georg Brandl3dbca812008-07-23 16:10:53 +00001105 # config5 specifies a custom handler class to be loaded
1106 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1107
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001108 # config6 uses ', ' delimiters in the handlers and formatters sections
1109 config6 = """
1110 [loggers]
1111 keys=root,parser
1112
1113 [handlers]
1114 keys=hand1, hand2
1115
1116 [formatters]
1117 keys=form1, form2
1118
1119 [logger_root]
1120 level=WARNING
1121 handlers=
1122
1123 [logger_parser]
1124 level=DEBUG
1125 handlers=hand1
1126 propagate=1
1127 qualname=compiler.parser
1128
1129 [handler_hand1]
1130 class=StreamHandler
1131 level=NOTSET
1132 formatter=form1
1133 args=(sys.stdout,)
1134
1135 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001136 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001137 level=NOTSET
1138 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001139 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001140
1141 [formatter_form1]
1142 format=%(levelname)s ++ %(message)s
1143 datefmt=
1144
1145 [formatter_form2]
1146 format=%(message)s
1147 datefmt=
1148 """
1149
Vinay Sajip3f84b072011-03-07 17:49:33 +00001150 # config7 adds a compiler logger.
1151 config7 = """
1152 [loggers]
1153 keys=root,parser,compiler
1154
1155 [handlers]
1156 keys=hand1
1157
1158 [formatters]
1159 keys=form1
1160
1161 [logger_root]
1162 level=WARNING
1163 handlers=hand1
1164
1165 [logger_compiler]
1166 level=DEBUG
1167 handlers=
1168 propagate=1
1169 qualname=compiler
1170
1171 [logger_parser]
1172 level=DEBUG
1173 handlers=
1174 propagate=1
1175 qualname=compiler.parser
1176
1177 [handler_hand1]
1178 class=StreamHandler
1179 level=NOTSET
1180 formatter=form1
1181 args=(sys.stdout,)
1182
1183 [formatter_form1]
1184 format=%(levelname)s ++ %(message)s
1185 datefmt=
1186 """
1187
Christian Heimes180510d2008-03-03 19:15:45 +00001188 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001189 file = io.StringIO(textwrap.dedent(conf))
1190 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001191
1192 def test_config0_ok(self):
1193 # A simple config file which overrides the default settings.
1194 with captured_stdout() as output:
1195 self.apply_config(self.config0)
1196 logger = logging.getLogger()
1197 # Won't output anything
1198 logger.info(self.next_message())
1199 # Outputs a message
1200 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001201 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001202 ('ERROR', '2'),
1203 ], stream=output)
1204 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001205 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001206
Georg Brandl3dbca812008-07-23 16:10:53 +00001207 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001208 # A config file defining a sub-parser as well.
1209 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001210 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001211 logger = logging.getLogger("compiler.parser")
1212 # Both will output a message
1213 logger.info(self.next_message())
1214 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001215 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001216 ('INFO', '1'),
1217 ('ERROR', '2'),
1218 ], stream=output)
1219 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001220 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001221
1222 def test_config2_failure(self):
1223 # A simple config file which overrides the default settings.
1224 self.assertRaises(Exception, self.apply_config, self.config2)
1225
1226 def test_config3_failure(self):
1227 # A simple config file which overrides the default settings.
1228 self.assertRaises(Exception, self.apply_config, self.config3)
1229
1230 def test_config4_ok(self):
1231 # A config file specifying a custom formatter class.
1232 with captured_stdout() as output:
1233 self.apply_config(self.config4)
1234 logger = logging.getLogger()
1235 try:
1236 raise RuntimeError()
1237 except RuntimeError:
1238 logging.exception("just testing")
1239 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001240 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001241 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1242 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001243 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001244
Georg Brandl3dbca812008-07-23 16:10:53 +00001245 def test_config5_ok(self):
1246 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001247
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001248 def test_config6_ok(self):
1249 self.test_config1_ok(config=self.config6)
1250
Vinay Sajip3f84b072011-03-07 17:49:33 +00001251 def test_config7_ok(self):
1252 with captured_stdout() as output:
1253 self.apply_config(self.config1a)
1254 logger = logging.getLogger("compiler.parser")
1255 # See issue #11424. compiler-hyphenated sorts
1256 # between compiler and compiler.xyz and this
1257 # was preventing compiler.xyz from being included
1258 # in the child loggers of compiler because of an
1259 # overzealous loop termination condition.
1260 hyphenated = logging.getLogger('compiler-hyphenated')
1261 # All will output a message
1262 logger.info(self.next_message())
1263 logger.error(self.next_message())
1264 hyphenated.critical(self.next_message())
1265 self.assert_log_lines([
1266 ('INFO', '1'),
1267 ('ERROR', '2'),
1268 ('CRITICAL', '3'),
1269 ], stream=output)
1270 # Original logger output is empty.
1271 self.assert_log_lines([])
1272 with captured_stdout() as output:
1273 self.apply_config(self.config7)
1274 logger = logging.getLogger("compiler.parser")
1275 self.assertFalse(logger.disabled)
1276 # Both will output a message
1277 logger.info(self.next_message())
1278 logger.error(self.next_message())
1279 logger = logging.getLogger("compiler.lexer")
1280 # Both will output a message
1281 logger.info(self.next_message())
1282 logger.error(self.next_message())
1283 # Will not appear
1284 hyphenated.critical(self.next_message())
1285 self.assert_log_lines([
1286 ('INFO', '4'),
1287 ('ERROR', '5'),
1288 ('INFO', '6'),
1289 ('ERROR', '7'),
1290 ], stream=output)
1291 # Original logger output is empty.
1292 self.assert_log_lines([])
1293
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001294
Victor Stinner45df8202010-04-28 22:31:17 +00001295@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001296class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001297
Christian Heimes180510d2008-03-03 19:15:45 +00001298 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001299
Christian Heimes180510d2008-03-03 19:15:45 +00001300 def setUp(self):
1301 """Set up a TCP server to receive log messages, and a SocketHandler
1302 pointing to that server's address and port."""
1303 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001304 addr = ('localhost', 0)
1305 self.server = server = TestTCPServer(addr, self.handle_socket,
1306 0.01)
1307 server.start()
1308 server.ready.wait()
1309 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1310 server.port)
1311 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001312 self.root_logger.removeHandler(self.root_logger.handlers[0])
1313 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001314 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001315
Christian Heimes180510d2008-03-03 19:15:45 +00001316 def tearDown(self):
1317 """Shutdown the TCP server."""
1318 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001319 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001320 self.root_logger.removeHandler(self.sock_hdlr)
1321 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001322 finally:
1323 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001324
Vinay Sajip7367d082011-05-02 13:17:27 +01001325 def handle_socket(self, request):
1326 conn = request.connection
1327 while True:
1328 chunk = conn.recv(4)
1329 if len(chunk) < 4:
1330 break
1331 slen = struct.unpack(">L", chunk)[0]
1332 chunk = conn.recv(slen)
1333 while len(chunk) < slen:
1334 chunk = chunk + conn.recv(slen - len(chunk))
1335 obj = pickle.loads(chunk)
1336 record = logging.makeLogRecord(obj)
1337 self.log_output += record.msg + '\n'
1338 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001339
Christian Heimes180510d2008-03-03 19:15:45 +00001340 def test_output(self):
1341 # The log message sent to the SocketHandler is properly received.
1342 logger = logging.getLogger("tcp")
1343 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001344 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001345 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001346 self.handled.acquire()
1347 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001348
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001349 def test_noserver(self):
1350 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001351 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001352 #The logging call should try to connect, which should fail
1353 try:
1354 raise RuntimeError('Deliberate mistake')
1355 except RuntimeError:
1356 self.root_logger.exception('Never sent')
1357 self.root_logger.error('Never sent, either')
1358 now = time.time()
1359 self.assertTrue(self.sock_hdlr.retryTime > now)
1360 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1361 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001362
Vinay Sajip7367d082011-05-02 13:17:27 +01001363
1364@unittest.skipUnless(threading, 'Threading required for this test.')
1365class DatagramHandlerTest(BaseTest):
1366
1367 """Test for DatagramHandler."""
1368
1369 def setUp(self):
1370 """Set up a UDP server to receive log messages, and a DatagramHandler
1371 pointing to that server's address and port."""
1372 BaseTest.setUp(self)
1373 addr = ('localhost', 0)
1374 self.server = server = TestUDPServer(addr, self.handle_datagram,
1375 0.01)
1376 server.start()
1377 server.ready.wait()
1378 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1379 server.port)
1380 self.log_output = ''
1381 self.root_logger.removeHandler(self.root_logger.handlers[0])
1382 self.root_logger.addHandler(self.sock_hdlr)
1383 self.handled = threading.Event()
1384
1385 def tearDown(self):
1386 """Shutdown the UDP server."""
1387 try:
1388 self.server.stop(2.0)
1389 self.root_logger.removeHandler(self.sock_hdlr)
1390 self.sock_hdlr.close()
1391 finally:
1392 BaseTest.tearDown(self)
1393
1394 def handle_datagram(self, request):
1395 slen = struct.pack('>L', 0) # length of prefix
1396 packet = request.packet[len(slen):]
1397 obj = pickle.loads(packet)
1398 record = logging.makeLogRecord(obj)
1399 self.log_output += record.msg + '\n'
1400 self.handled.set()
1401
1402 def test_output(self):
1403 # The log message sent to the DatagramHandler is properly received.
1404 logger = logging.getLogger("udp")
1405 logger.error("spam")
1406 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001407 self.handled.clear()
1408 logger.error("eggs")
1409 self.handled.wait()
1410 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001411
1412
1413@unittest.skipUnless(threading, 'Threading required for this test.')
1414class SysLogHandlerTest(BaseTest):
1415
1416 """Test for SysLogHandler using UDP."""
1417
1418 def setUp(self):
1419 """Set up a UDP server to receive log messages, and a SysLogHandler
1420 pointing to that server's address and port."""
1421 BaseTest.setUp(self)
1422 addr = ('localhost', 0)
1423 self.server = server = TestUDPServer(addr, self.handle_datagram,
1424 0.01)
1425 server.start()
1426 server.ready.wait()
1427 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1428 server.port))
1429 self.log_output = ''
1430 self.root_logger.removeHandler(self.root_logger.handlers[0])
1431 self.root_logger.addHandler(self.sl_hdlr)
1432 self.handled = threading.Event()
1433
1434 def tearDown(self):
1435 """Shutdown the UDP server."""
1436 try:
1437 self.server.stop(2.0)
1438 self.root_logger.removeHandler(self.sl_hdlr)
1439 self.sl_hdlr.close()
1440 finally:
1441 BaseTest.tearDown(self)
1442
1443 def handle_datagram(self, request):
1444 self.log_output = request.packet
1445 self.handled.set()
1446
1447 def test_output(self):
1448 # The log message sent to the SysLogHandler is properly received.
1449 logger = logging.getLogger("slh")
1450 logger.error("sp\xe4m")
1451 self.handled.wait()
1452 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1453
1454
1455@unittest.skipUnless(threading, 'Threading required for this test.')
1456class HTTPHandlerTest(BaseTest):
1457
1458 """Test for HTTPHandler."""
1459
1460 def setUp(self):
1461 """Set up an HTTP server to receive log messages, and a HTTPHandler
1462 pointing to that server's address and port."""
1463 BaseTest.setUp(self)
1464 addr = ('localhost', 0)
1465 self.server = server = TestHTTPServer(addr, self.handle_request,
1466 0.01)
1467 server.start()
1468 server.ready.wait()
1469 host = 'localhost:%d' % server.server_port
1470 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob')
1471 self.log_data = None
1472 self.root_logger.removeHandler(self.root_logger.handlers[0])
1473 self.root_logger.addHandler(self.h_hdlr)
1474 self.handled = threading.Event()
1475
1476 def tearDown(self):
1477 """Shutdown the UDP server."""
1478 try:
1479 self.server.stop(2.0)
1480 self.root_logger.removeHandler(self.h_hdlr)
1481 self.h_hdlr.close()
1482 finally:
1483 BaseTest.tearDown(self)
1484
1485 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001486 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001487 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001488 if self.command == 'POST':
1489 try:
1490 rlen = int(request.headers['Content-Length'])
1491 self.post_data = request.rfile.read(rlen)
1492 except:
1493 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001494 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001495 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001496 self.handled.set()
1497
1498 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001499 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001500 logger = logging.getLogger("http")
Vinay Sajip0372e102011-05-05 12:59:14 +01001501 for method in ('GET', 'POST'):
1502 self.h_hdlr.method = method
1503 msg = "sp\xe4m"
1504 logger.error(msg)
1505 self.handled.wait()
1506 self.assertEqual(self.log_data.path, '/frob')
1507 self.assertEqual(self.command, method)
1508 if method == 'GET':
1509 d = parse_qs(self.log_data.query)
1510 else:
1511 d = parse_qs(self.post_data.decode('utf-8'))
1512 self.assertEqual(d['name'], ['http'])
1513 self.assertEqual(d['funcName'], ['test_output'])
1514 self.assertEqual(d['msg'], [msg])
Vinay Sajip7367d082011-05-02 13:17:27 +01001515
Christian Heimes180510d2008-03-03 19:15:45 +00001516class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001517
Christian Heimes180510d2008-03-03 19:15:45 +00001518 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001519
Christian Heimes180510d2008-03-03 19:15:45 +00001520 def setUp(self):
1521 """Create a dict to remember potentially destroyed objects."""
1522 BaseTest.setUp(self)
1523 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001524
Christian Heimes180510d2008-03-03 19:15:45 +00001525 def _watch_for_survival(self, *args):
1526 """Watch the given objects for survival, by creating weakrefs to
1527 them."""
1528 for obj in args:
1529 key = id(obj), repr(obj)
1530 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001531
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001532 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001533 """Assert that all objects watched for survival have survived."""
1534 # Trigger cycle breaking.
1535 gc.collect()
1536 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001537 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001538 if ref() is None:
1539 dead.append(repr_)
1540 if dead:
1541 self.fail("%d objects should have survived "
1542 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001543
Christian Heimes180510d2008-03-03 19:15:45 +00001544 def test_persistent_loggers(self):
1545 # Logger objects are persistent and retain their configuration, even
1546 # if visible references are destroyed.
1547 self.root_logger.setLevel(logging.INFO)
1548 foo = logging.getLogger("foo")
1549 self._watch_for_survival(foo)
1550 foo.setLevel(logging.DEBUG)
1551 self.root_logger.debug(self.next_message())
1552 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001553 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001554 ('foo', 'DEBUG', '2'),
1555 ])
1556 del foo
1557 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001558 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001559 # foo has retained its settings.
1560 bar = logging.getLogger("foo")
1561 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001562 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001563 ('foo', 'DEBUG', '2'),
1564 ('foo', 'DEBUG', '3'),
1565 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001566
Benjamin Petersonf91df042009-02-13 02:50:59 +00001567
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001568class EncodingTest(BaseTest):
1569 def test_encoding_plain_file(self):
1570 # In Python 2.x, a plain file object is treated as having no encoding.
1571 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001572 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1573 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001574 # the non-ascii data we write to the log.
1575 data = "foo\x80"
1576 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001577 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001578 log.addHandler(handler)
1579 try:
1580 # write non-ascii data to the log.
1581 log.warning(data)
1582 finally:
1583 log.removeHandler(handler)
1584 handler.close()
1585 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001586 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001587 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001588 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001589 finally:
1590 f.close()
1591 finally:
1592 if os.path.isfile(fn):
1593 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001594
Benjamin Petersonf91df042009-02-13 02:50:59 +00001595 def test_encoding_cyrillic_unicode(self):
1596 log = logging.getLogger("test")
1597 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1598 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1599 #Ensure it's written in a Cyrillic encoding
1600 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001601 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001602 stream = io.BytesIO()
1603 writer = writer_class(stream, 'strict')
1604 handler = logging.StreamHandler(writer)
1605 log.addHandler(handler)
1606 try:
1607 log.warning(message)
1608 finally:
1609 log.removeHandler(handler)
1610 handler.close()
1611 # check we wrote exactly those bytes, ignoring trailing \n etc
1612 s = stream.getvalue()
1613 #Compare against what the data should be when encoded in CP-1251
1614 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1615
1616
Georg Brandlf9734072008-12-07 15:30:06 +00001617class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001618
Georg Brandlf9734072008-12-07 15:30:06 +00001619 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001620 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001621 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001622 self.addCleanup(lambda: logging.captureWarnings(False))
1623 warnings.filterwarnings("always", category=UserWarning)
1624 stream = io.StringIO()
1625 h = logging.StreamHandler(stream)
1626 logger = logging.getLogger("py.warnings")
1627 logger.addHandler(h)
1628 warnings.warn("I'm warning you...")
1629 logger.removeHandler(h)
1630 s = stream.getvalue()
1631 h.close()
1632 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001633
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001634 #See if an explicit file uses the original implementation
1635 a_file = io.StringIO()
1636 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1637 a_file, "Dummy line")
1638 s = a_file.getvalue()
1639 a_file.close()
1640 self.assertEqual(s,
1641 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1642
1643 def test_warnings_no_handlers(self):
1644 with warnings.catch_warnings():
1645 logging.captureWarnings(True)
1646 self.addCleanup(lambda: logging.captureWarnings(False))
1647
1648 # confirm our assumption: no loggers are set
1649 logger = logging.getLogger("py.warnings")
1650 assert logger.handlers == []
1651
1652 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1653 self.assertTrue(len(logger.handlers) == 1)
1654 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001655
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001656
1657def formatFunc(format, datefmt=None):
1658 return logging.Formatter(format, datefmt)
1659
1660def handlerFunc():
1661 return logging.StreamHandler()
1662
1663class CustomHandler(logging.StreamHandler):
1664 pass
1665
1666class ConfigDictTest(BaseTest):
1667
1668 """Reading logging config from a dictionary."""
1669
1670 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1671
1672 # config0 is a standard configuration.
1673 config0 = {
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 'handlers' : {
1681 'hand1' : {
1682 'class' : 'logging.StreamHandler',
1683 'formatter' : 'form1',
1684 'level' : 'NOTSET',
1685 'stream' : 'ext://sys.stdout',
1686 },
1687 },
1688 'root' : {
1689 'level' : 'WARNING',
1690 'handlers' : ['hand1'],
1691 },
1692 }
1693
1694 # config1 adds a little to the standard configuration.
1695 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001696 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001697 'formatters': {
1698 'form1' : {
1699 'format' : '%(levelname)s ++ %(message)s',
1700 },
1701 },
1702 'handlers' : {
1703 'hand1' : {
1704 'class' : 'logging.StreamHandler',
1705 'formatter' : 'form1',
1706 'level' : 'NOTSET',
1707 'stream' : 'ext://sys.stdout',
1708 },
1709 },
1710 'loggers' : {
1711 'compiler.parser' : {
1712 'level' : 'DEBUG',
1713 'handlers' : ['hand1'],
1714 },
1715 },
1716 'root' : {
1717 'level' : 'WARNING',
1718 },
1719 }
1720
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001721 # config1a moves the handler to the root. Used with config8a
1722 config1a = {
1723 'version': 1,
1724 'formatters': {
1725 'form1' : {
1726 'format' : '%(levelname)s ++ %(message)s',
1727 },
1728 },
1729 'handlers' : {
1730 'hand1' : {
1731 'class' : 'logging.StreamHandler',
1732 'formatter' : 'form1',
1733 'level' : 'NOTSET',
1734 'stream' : 'ext://sys.stdout',
1735 },
1736 },
1737 'loggers' : {
1738 'compiler.parser' : {
1739 'level' : 'DEBUG',
1740 },
1741 },
1742 'root' : {
1743 'level' : 'WARNING',
1744 'handlers' : ['hand1'],
1745 },
1746 }
1747
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001748 # config2 has a subtle configuration error that should be reported
1749 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001750 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001751 'formatters': {
1752 'form1' : {
1753 'format' : '%(levelname)s ++ %(message)s',
1754 },
1755 },
1756 'handlers' : {
1757 'hand1' : {
1758 'class' : 'logging.StreamHandler',
1759 'formatter' : 'form1',
1760 'level' : 'NOTSET',
1761 'stream' : 'ext://sys.stdbout',
1762 },
1763 },
1764 'loggers' : {
1765 'compiler.parser' : {
1766 'level' : 'DEBUG',
1767 'handlers' : ['hand1'],
1768 },
1769 },
1770 'root' : {
1771 'level' : 'WARNING',
1772 },
1773 }
1774
1775 #As config1 but with a misspelt level on a handler
1776 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001777 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001778 'formatters': {
1779 'form1' : {
1780 'format' : '%(levelname)s ++ %(message)s',
1781 },
1782 },
1783 'handlers' : {
1784 'hand1' : {
1785 'class' : 'logging.StreamHandler',
1786 'formatter' : 'form1',
1787 'level' : 'NTOSET',
1788 'stream' : 'ext://sys.stdout',
1789 },
1790 },
1791 'loggers' : {
1792 'compiler.parser' : {
1793 'level' : 'DEBUG',
1794 'handlers' : ['hand1'],
1795 },
1796 },
1797 'root' : {
1798 'level' : 'WARNING',
1799 },
1800 }
1801
1802
1803 #As config1 but with a misspelt level on a logger
1804 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001805 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001806 'formatters': {
1807 'form1' : {
1808 'format' : '%(levelname)s ++ %(message)s',
1809 },
1810 },
1811 'handlers' : {
1812 'hand1' : {
1813 'class' : 'logging.StreamHandler',
1814 'formatter' : 'form1',
1815 'level' : 'NOTSET',
1816 'stream' : 'ext://sys.stdout',
1817 },
1818 },
1819 'loggers' : {
1820 'compiler.parser' : {
1821 'level' : 'DEBUG',
1822 'handlers' : ['hand1'],
1823 },
1824 },
1825 'root' : {
1826 'level' : 'WRANING',
1827 },
1828 }
1829
1830 # config3 has a less subtle configuration error
1831 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001832 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001833 'formatters': {
1834 'form1' : {
1835 'format' : '%(levelname)s ++ %(message)s',
1836 },
1837 },
1838 'handlers' : {
1839 'hand1' : {
1840 'class' : 'logging.StreamHandler',
1841 'formatter' : 'misspelled_name',
1842 'level' : 'NOTSET',
1843 'stream' : 'ext://sys.stdout',
1844 },
1845 },
1846 'loggers' : {
1847 'compiler.parser' : {
1848 'level' : 'DEBUG',
1849 'handlers' : ['hand1'],
1850 },
1851 },
1852 'root' : {
1853 'level' : 'WARNING',
1854 },
1855 }
1856
1857 # config4 specifies a custom formatter class to be loaded
1858 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001859 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001860 'formatters': {
1861 'form1' : {
1862 '()' : __name__ + '.ExceptionFormatter',
1863 'format' : '%(levelname)s:%(name)s:%(message)s',
1864 },
1865 },
1866 'handlers' : {
1867 'hand1' : {
1868 'class' : 'logging.StreamHandler',
1869 'formatter' : 'form1',
1870 'level' : 'NOTSET',
1871 'stream' : 'ext://sys.stdout',
1872 },
1873 },
1874 'root' : {
1875 'level' : 'NOTSET',
1876 'handlers' : ['hand1'],
1877 },
1878 }
1879
1880 # As config4 but using an actual callable rather than a string
1881 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001882 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001883 'formatters': {
1884 'form1' : {
1885 '()' : ExceptionFormatter,
1886 'format' : '%(levelname)s:%(name)s:%(message)s',
1887 },
1888 'form2' : {
1889 '()' : __name__ + '.formatFunc',
1890 'format' : '%(levelname)s:%(name)s:%(message)s',
1891 },
1892 'form3' : {
1893 '()' : formatFunc,
1894 'format' : '%(levelname)s:%(name)s:%(message)s',
1895 },
1896 },
1897 'handlers' : {
1898 'hand1' : {
1899 'class' : 'logging.StreamHandler',
1900 'formatter' : 'form1',
1901 'level' : 'NOTSET',
1902 'stream' : 'ext://sys.stdout',
1903 },
1904 'hand2' : {
1905 '()' : handlerFunc,
1906 },
1907 },
1908 'root' : {
1909 'level' : 'NOTSET',
1910 'handlers' : ['hand1'],
1911 },
1912 }
1913
1914 # config5 specifies a custom handler class to be loaded
1915 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001916 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001917 'formatters': {
1918 'form1' : {
1919 'format' : '%(levelname)s ++ %(message)s',
1920 },
1921 },
1922 'handlers' : {
1923 'hand1' : {
1924 'class' : __name__ + '.CustomHandler',
1925 'formatter' : 'form1',
1926 'level' : 'NOTSET',
1927 'stream' : 'ext://sys.stdout',
1928 },
1929 },
1930 'loggers' : {
1931 'compiler.parser' : {
1932 'level' : 'DEBUG',
1933 'handlers' : ['hand1'],
1934 },
1935 },
1936 'root' : {
1937 'level' : 'WARNING',
1938 },
1939 }
1940
1941 # config6 specifies a custom handler class to be loaded
1942 # but has bad arguments
1943 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001944 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001945 'formatters': {
1946 'form1' : {
1947 'format' : '%(levelname)s ++ %(message)s',
1948 },
1949 },
1950 'handlers' : {
1951 'hand1' : {
1952 'class' : __name__ + '.CustomHandler',
1953 'formatter' : 'form1',
1954 'level' : 'NOTSET',
1955 'stream' : 'ext://sys.stdout',
1956 '9' : 'invalid parameter name',
1957 },
1958 },
1959 'loggers' : {
1960 'compiler.parser' : {
1961 'level' : 'DEBUG',
1962 'handlers' : ['hand1'],
1963 },
1964 },
1965 'root' : {
1966 'level' : 'WARNING',
1967 },
1968 }
1969
1970 #config 7 does not define compiler.parser but defines compiler.lexer
1971 #so compiler.parser should be disabled after applying it
1972 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001973 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001974 'formatters': {
1975 'form1' : {
1976 'format' : '%(levelname)s ++ %(message)s',
1977 },
1978 },
1979 'handlers' : {
1980 'hand1' : {
1981 'class' : 'logging.StreamHandler',
1982 'formatter' : 'form1',
1983 'level' : 'NOTSET',
1984 'stream' : 'ext://sys.stdout',
1985 },
1986 },
1987 'loggers' : {
1988 'compiler.lexer' : {
1989 'level' : 'DEBUG',
1990 'handlers' : ['hand1'],
1991 },
1992 },
1993 'root' : {
1994 'level' : 'WARNING',
1995 },
1996 }
1997
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001998 # config8 defines both compiler and compiler.lexer
1999 # so compiler.parser should not be disabled (since
2000 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002001 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002002 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002003 'disable_existing_loggers' : False,
2004 'formatters': {
2005 'form1' : {
2006 'format' : '%(levelname)s ++ %(message)s',
2007 },
2008 },
2009 'handlers' : {
2010 'hand1' : {
2011 'class' : 'logging.StreamHandler',
2012 'formatter' : 'form1',
2013 'level' : 'NOTSET',
2014 'stream' : 'ext://sys.stdout',
2015 },
2016 },
2017 'loggers' : {
2018 'compiler' : {
2019 'level' : 'DEBUG',
2020 'handlers' : ['hand1'],
2021 },
2022 'compiler.lexer' : {
2023 },
2024 },
2025 'root' : {
2026 'level' : 'WARNING',
2027 },
2028 }
2029
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002030 # config8a disables existing loggers
2031 config8a = {
2032 'version': 1,
2033 'disable_existing_loggers' : True,
2034 'formatters': {
2035 'form1' : {
2036 'format' : '%(levelname)s ++ %(message)s',
2037 },
2038 },
2039 'handlers' : {
2040 'hand1' : {
2041 'class' : 'logging.StreamHandler',
2042 'formatter' : 'form1',
2043 'level' : 'NOTSET',
2044 'stream' : 'ext://sys.stdout',
2045 },
2046 },
2047 'loggers' : {
2048 'compiler' : {
2049 'level' : 'DEBUG',
2050 'handlers' : ['hand1'],
2051 },
2052 'compiler.lexer' : {
2053 },
2054 },
2055 'root' : {
2056 'level' : 'WARNING',
2057 },
2058 }
2059
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002060 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002061 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002062 'formatters': {
2063 'form1' : {
2064 'format' : '%(levelname)s ++ %(message)s',
2065 },
2066 },
2067 'handlers' : {
2068 'hand1' : {
2069 'class' : 'logging.StreamHandler',
2070 'formatter' : 'form1',
2071 'level' : 'WARNING',
2072 'stream' : 'ext://sys.stdout',
2073 },
2074 },
2075 'loggers' : {
2076 'compiler.parser' : {
2077 'level' : 'WARNING',
2078 'handlers' : ['hand1'],
2079 },
2080 },
2081 'root' : {
2082 'level' : 'NOTSET',
2083 },
2084 }
2085
2086 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002087 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002088 'incremental' : True,
2089 'handlers' : {
2090 'hand1' : {
2091 'level' : 'WARNING',
2092 },
2093 },
2094 'loggers' : {
2095 'compiler.parser' : {
2096 'level' : 'INFO',
2097 },
2098 },
2099 }
2100
2101 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002102 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002103 'incremental' : True,
2104 'handlers' : {
2105 'hand1' : {
2106 'level' : 'INFO',
2107 },
2108 },
2109 'loggers' : {
2110 'compiler.parser' : {
2111 'level' : 'INFO',
2112 },
2113 },
2114 }
2115
2116 #As config1 but with a filter added
2117 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002118 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002119 'formatters': {
2120 'form1' : {
2121 'format' : '%(levelname)s ++ %(message)s',
2122 },
2123 },
2124 'filters' : {
2125 'filt1' : {
2126 'name' : 'compiler.parser',
2127 },
2128 },
2129 'handlers' : {
2130 'hand1' : {
2131 'class' : 'logging.StreamHandler',
2132 'formatter' : 'form1',
2133 'level' : 'NOTSET',
2134 'stream' : 'ext://sys.stdout',
2135 'filters' : ['filt1'],
2136 },
2137 },
2138 'loggers' : {
2139 'compiler.parser' : {
2140 'level' : 'DEBUG',
2141 'filters' : ['filt1'],
2142 },
2143 },
2144 'root' : {
2145 'level' : 'WARNING',
2146 'handlers' : ['hand1'],
2147 },
2148 }
2149
2150 #As config1 but using cfg:// references
2151 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002152 'version': 1,
2153 'true_formatters': {
2154 'form1' : {
2155 'format' : '%(levelname)s ++ %(message)s',
2156 },
2157 },
2158 'handler_configs': {
2159 'hand1' : {
2160 'class' : 'logging.StreamHandler',
2161 'formatter' : 'form1',
2162 'level' : 'NOTSET',
2163 'stream' : 'ext://sys.stdout',
2164 },
2165 },
2166 'formatters' : 'cfg://true_formatters',
2167 'handlers' : {
2168 'hand1' : 'cfg://handler_configs[hand1]',
2169 },
2170 'loggers' : {
2171 'compiler.parser' : {
2172 'level' : 'DEBUG',
2173 'handlers' : ['hand1'],
2174 },
2175 },
2176 'root' : {
2177 'level' : 'WARNING',
2178 },
2179 }
2180
2181 #As config11 but missing the version key
2182 config12 = {
2183 'true_formatters': {
2184 'form1' : {
2185 'format' : '%(levelname)s ++ %(message)s',
2186 },
2187 },
2188 'handler_configs': {
2189 'hand1' : {
2190 'class' : 'logging.StreamHandler',
2191 'formatter' : 'form1',
2192 'level' : 'NOTSET',
2193 'stream' : 'ext://sys.stdout',
2194 },
2195 },
2196 'formatters' : 'cfg://true_formatters',
2197 'handlers' : {
2198 'hand1' : 'cfg://handler_configs[hand1]',
2199 },
2200 'loggers' : {
2201 'compiler.parser' : {
2202 'level' : 'DEBUG',
2203 'handlers' : ['hand1'],
2204 },
2205 },
2206 'root' : {
2207 'level' : 'WARNING',
2208 },
2209 }
2210
2211 #As config11 but using an unsupported version
2212 config13 = {
2213 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002214 'true_formatters': {
2215 'form1' : {
2216 'format' : '%(levelname)s ++ %(message)s',
2217 },
2218 },
2219 'handler_configs': {
2220 'hand1' : {
2221 'class' : 'logging.StreamHandler',
2222 'formatter' : 'form1',
2223 'level' : 'NOTSET',
2224 'stream' : 'ext://sys.stdout',
2225 },
2226 },
2227 'formatters' : 'cfg://true_formatters',
2228 'handlers' : {
2229 'hand1' : 'cfg://handler_configs[hand1]',
2230 },
2231 'loggers' : {
2232 'compiler.parser' : {
2233 'level' : 'DEBUG',
2234 'handlers' : ['hand1'],
2235 },
2236 },
2237 'root' : {
2238 'level' : 'WARNING',
2239 },
2240 }
2241
2242 def apply_config(self, conf):
2243 logging.config.dictConfig(conf)
2244
2245 def test_config0_ok(self):
2246 # A simple config which overrides the default settings.
2247 with captured_stdout() as output:
2248 self.apply_config(self.config0)
2249 logger = logging.getLogger()
2250 # Won't output anything
2251 logger.info(self.next_message())
2252 # Outputs a message
2253 logger.error(self.next_message())
2254 self.assert_log_lines([
2255 ('ERROR', '2'),
2256 ], stream=output)
2257 # Original logger output is empty.
2258 self.assert_log_lines([])
2259
2260 def test_config1_ok(self, config=config1):
2261 # A config defining a sub-parser as well.
2262 with captured_stdout() as output:
2263 self.apply_config(config)
2264 logger = logging.getLogger("compiler.parser")
2265 # Both will output a message
2266 logger.info(self.next_message())
2267 logger.error(self.next_message())
2268 self.assert_log_lines([
2269 ('INFO', '1'),
2270 ('ERROR', '2'),
2271 ], stream=output)
2272 # Original logger output is empty.
2273 self.assert_log_lines([])
2274
2275 def test_config2_failure(self):
2276 # A simple config which overrides the default settings.
2277 self.assertRaises(Exception, self.apply_config, self.config2)
2278
2279 def test_config2a_failure(self):
2280 # A simple config which overrides the default settings.
2281 self.assertRaises(Exception, self.apply_config, self.config2a)
2282
2283 def test_config2b_failure(self):
2284 # A simple config which overrides the default settings.
2285 self.assertRaises(Exception, self.apply_config, self.config2b)
2286
2287 def test_config3_failure(self):
2288 # A simple config which overrides the default settings.
2289 self.assertRaises(Exception, self.apply_config, self.config3)
2290
2291 def test_config4_ok(self):
2292 # A config specifying a custom formatter class.
2293 with captured_stdout() as output:
2294 self.apply_config(self.config4)
2295 #logger = logging.getLogger()
2296 try:
2297 raise RuntimeError()
2298 except RuntimeError:
2299 logging.exception("just testing")
2300 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002301 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002302 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2303 # Original logger output is empty
2304 self.assert_log_lines([])
2305
2306 def test_config4a_ok(self):
2307 # A config specifying a custom formatter class.
2308 with captured_stdout() as output:
2309 self.apply_config(self.config4a)
2310 #logger = logging.getLogger()
2311 try:
2312 raise RuntimeError()
2313 except RuntimeError:
2314 logging.exception("just testing")
2315 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002316 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002317 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2318 # Original logger output is empty
2319 self.assert_log_lines([])
2320
2321 def test_config5_ok(self):
2322 self.test_config1_ok(config=self.config5)
2323
2324 def test_config6_failure(self):
2325 self.assertRaises(Exception, self.apply_config, self.config6)
2326
2327 def test_config7_ok(self):
2328 with captured_stdout() as output:
2329 self.apply_config(self.config1)
2330 logger = logging.getLogger("compiler.parser")
2331 # Both will output a message
2332 logger.info(self.next_message())
2333 logger.error(self.next_message())
2334 self.assert_log_lines([
2335 ('INFO', '1'),
2336 ('ERROR', '2'),
2337 ], stream=output)
2338 # Original logger output is empty.
2339 self.assert_log_lines([])
2340 with captured_stdout() as output:
2341 self.apply_config(self.config7)
2342 logger = logging.getLogger("compiler.parser")
2343 self.assertTrue(logger.disabled)
2344 logger = logging.getLogger("compiler.lexer")
2345 # Both will output a message
2346 logger.info(self.next_message())
2347 logger.error(self.next_message())
2348 self.assert_log_lines([
2349 ('INFO', '3'),
2350 ('ERROR', '4'),
2351 ], stream=output)
2352 # Original logger output is empty.
2353 self.assert_log_lines([])
2354
2355 #Same as test_config_7_ok but don't disable old loggers.
2356 def test_config_8_ok(self):
2357 with captured_stdout() as output:
2358 self.apply_config(self.config1)
2359 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002360 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002361 logger.info(self.next_message())
2362 logger.error(self.next_message())
2363 self.assert_log_lines([
2364 ('INFO', '1'),
2365 ('ERROR', '2'),
2366 ], stream=output)
2367 # Original logger output is empty.
2368 self.assert_log_lines([])
2369 with captured_stdout() as output:
2370 self.apply_config(self.config8)
2371 logger = logging.getLogger("compiler.parser")
2372 self.assertFalse(logger.disabled)
2373 # Both will output a message
2374 logger.info(self.next_message())
2375 logger.error(self.next_message())
2376 logger = logging.getLogger("compiler.lexer")
2377 # Both will output a message
2378 logger.info(self.next_message())
2379 logger.error(self.next_message())
2380 self.assert_log_lines([
2381 ('INFO', '3'),
2382 ('ERROR', '4'),
2383 ('INFO', '5'),
2384 ('ERROR', '6'),
2385 ], stream=output)
2386 # Original logger output is empty.
2387 self.assert_log_lines([])
2388
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002389 def test_config_8a_ok(self):
2390 with captured_stdout() as output:
2391 self.apply_config(self.config1a)
2392 logger = logging.getLogger("compiler.parser")
2393 # See issue #11424. compiler-hyphenated sorts
2394 # between compiler and compiler.xyz and this
2395 # was preventing compiler.xyz from being included
2396 # in the child loggers of compiler because of an
2397 # overzealous loop termination condition.
2398 hyphenated = logging.getLogger('compiler-hyphenated')
2399 # All will output a message
2400 logger.info(self.next_message())
2401 logger.error(self.next_message())
2402 hyphenated.critical(self.next_message())
2403 self.assert_log_lines([
2404 ('INFO', '1'),
2405 ('ERROR', '2'),
2406 ('CRITICAL', '3'),
2407 ], stream=output)
2408 # Original logger output is empty.
2409 self.assert_log_lines([])
2410 with captured_stdout() as output:
2411 self.apply_config(self.config8a)
2412 logger = logging.getLogger("compiler.parser")
2413 self.assertFalse(logger.disabled)
2414 # Both will output a message
2415 logger.info(self.next_message())
2416 logger.error(self.next_message())
2417 logger = logging.getLogger("compiler.lexer")
2418 # Both will output a message
2419 logger.info(self.next_message())
2420 logger.error(self.next_message())
2421 # Will not appear
2422 hyphenated.critical(self.next_message())
2423 self.assert_log_lines([
2424 ('INFO', '4'),
2425 ('ERROR', '5'),
2426 ('INFO', '6'),
2427 ('ERROR', '7'),
2428 ], stream=output)
2429 # Original logger output is empty.
2430 self.assert_log_lines([])
2431
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002432 def test_config_9_ok(self):
2433 with captured_stdout() as output:
2434 self.apply_config(self.config9)
2435 logger = logging.getLogger("compiler.parser")
2436 #Nothing will be output since both handler and logger are set to WARNING
2437 logger.info(self.next_message())
2438 self.assert_log_lines([], stream=output)
2439 self.apply_config(self.config9a)
2440 #Nothing will be output since both handler is still set to WARNING
2441 logger.info(self.next_message())
2442 self.assert_log_lines([], stream=output)
2443 self.apply_config(self.config9b)
2444 #Message should now be output
2445 logger.info(self.next_message())
2446 self.assert_log_lines([
2447 ('INFO', '3'),
2448 ], stream=output)
2449
2450 def test_config_10_ok(self):
2451 with captured_stdout() as output:
2452 self.apply_config(self.config10)
2453 logger = logging.getLogger("compiler.parser")
2454 logger.warning(self.next_message())
2455 logger = logging.getLogger('compiler')
2456 #Not output, because filtered
2457 logger.warning(self.next_message())
2458 logger = logging.getLogger('compiler.lexer')
2459 #Not output, because filtered
2460 logger.warning(self.next_message())
2461 logger = logging.getLogger("compiler.parser.codegen")
2462 #Output, as not filtered
2463 logger.error(self.next_message())
2464 self.assert_log_lines([
2465 ('WARNING', '1'),
2466 ('ERROR', '4'),
2467 ], stream=output)
2468
2469 def test_config11_ok(self):
2470 self.test_config1_ok(self.config11)
2471
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002472 def test_config12_failure(self):
2473 self.assertRaises(Exception, self.apply_config, self.config12)
2474
2475 def test_config13_failure(self):
2476 self.assertRaises(Exception, self.apply_config, self.config13)
2477
Victor Stinner45df8202010-04-28 22:31:17 +00002478 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002479 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002480 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002481 # Ask for a randomly assigned port (by using port 0)
2482 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002483 t.start()
2484 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002485 # Now get the port allocated
2486 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002487 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002488 try:
2489 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2490 sock.settimeout(2.0)
2491 sock.connect(('localhost', port))
2492
2493 slen = struct.pack('>L', len(text))
2494 s = slen + text
2495 sentsofar = 0
2496 left = len(s)
2497 while left > 0:
2498 sent = sock.send(s[sentsofar:])
2499 sentsofar += sent
2500 left -= sent
2501 sock.close()
2502 finally:
2503 t.ready.wait(2.0)
2504 logging.config.stopListening()
2505 t.join(2.0)
2506
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002507 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002508 def test_listen_config_10_ok(self):
2509 with captured_stdout() as output:
2510 self.setup_via_listener(json.dumps(self.config10))
2511 logger = logging.getLogger("compiler.parser")
2512 logger.warning(self.next_message())
2513 logger = logging.getLogger('compiler')
2514 #Not output, because filtered
2515 logger.warning(self.next_message())
2516 logger = logging.getLogger('compiler.lexer')
2517 #Not output, because filtered
2518 logger.warning(self.next_message())
2519 logger = logging.getLogger("compiler.parser.codegen")
2520 #Output, as not filtered
2521 logger.error(self.next_message())
2522 self.assert_log_lines([
2523 ('WARNING', '1'),
2524 ('ERROR', '4'),
2525 ], stream=output)
2526
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002527 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002528 def test_listen_config_1_ok(self):
2529 with captured_stdout() as output:
2530 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2531 logger = logging.getLogger("compiler.parser")
2532 # Both will output a message
2533 logger.info(self.next_message())
2534 logger.error(self.next_message())
2535 self.assert_log_lines([
2536 ('INFO', '1'),
2537 ('ERROR', '2'),
2538 ], stream=output)
2539 # Original logger output is empty.
2540 self.assert_log_lines([])
2541
Vinay Sajip373baef2011-04-26 20:05:24 +01002542 def test_baseconfig(self):
2543 d = {
2544 'atuple': (1, 2, 3),
2545 'alist': ['a', 'b', 'c'],
2546 'adict': {'d': 'e', 'f': 3 },
2547 'nest1': ('g', ('h', 'i'), 'j'),
2548 'nest2': ['k', ['l', 'm'], 'n'],
2549 'nest3': ['o', 'cfg://alist', 'p'],
2550 }
2551 bc = logging.config.BaseConfigurator(d)
2552 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2553 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2554 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2555 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2556 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2557 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2558 v = bc.convert('cfg://nest3')
2559 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2560 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2561 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2562 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002563
2564class ManagerTest(BaseTest):
2565 def test_manager_loggerclass(self):
2566 logged = []
2567
2568 class MyLogger(logging.Logger):
2569 def _log(self, level, msg, args, exc_info=None, extra=None):
2570 logged.append(msg)
2571
2572 man = logging.Manager(None)
2573 self.assertRaises(TypeError, man.setLoggerClass, int)
2574 man.setLoggerClass(MyLogger)
2575 logger = man.getLogger('test')
2576 logger.warning('should appear in logged')
2577 logging.warning('should not appear in logged')
2578
2579 self.assertEqual(logged, ['should appear in logged'])
2580
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002581 def test_set_log_record_factory(self):
2582 man = logging.Manager(None)
2583 expected = object()
2584 man.setLogRecordFactory(expected)
2585 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002586
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002587class ChildLoggerTest(BaseTest):
2588 def test_child_loggers(self):
2589 r = logging.getLogger()
2590 l1 = logging.getLogger('abc')
2591 l2 = logging.getLogger('def.ghi')
2592 c1 = r.getChild('xyz')
2593 c2 = r.getChild('uvw.xyz')
2594 self.assertTrue(c1 is logging.getLogger('xyz'))
2595 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2596 c1 = l1.getChild('def')
2597 c2 = c1.getChild('ghi')
2598 c3 = l1.getChild('def.ghi')
2599 self.assertTrue(c1 is logging.getLogger('abc.def'))
2600 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2601 self.assertTrue(c2 is c3)
2602
2603
Vinay Sajip6fac8172010-10-19 20:44:14 +00002604class DerivedLogRecord(logging.LogRecord):
2605 pass
2606
Vinay Sajip61561522010-12-03 11:50:38 +00002607class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002608
2609 def setUp(self):
2610 class CheckingFilter(logging.Filter):
2611 def __init__(self, cls):
2612 self.cls = cls
2613
2614 def filter(self, record):
2615 t = type(record)
2616 if t is not self.cls:
2617 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2618 self.cls)
2619 raise TypeError(msg)
2620 return True
2621
2622 BaseTest.setUp(self)
2623 self.filter = CheckingFilter(DerivedLogRecord)
2624 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002625 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002626
2627 def tearDown(self):
2628 self.root_logger.removeFilter(self.filter)
2629 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002630 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002631
2632 def test_logrecord_class(self):
2633 self.assertRaises(TypeError, self.root_logger.warning,
2634 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002635 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002636 self.root_logger.error(self.next_message())
2637 self.assert_log_lines([
2638 ('root', 'ERROR', '2'),
2639 ])
2640
2641
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002642class QueueHandlerTest(BaseTest):
2643 # Do not bother with a logger name group.
2644 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2645
2646 def setUp(self):
2647 BaseTest.setUp(self)
2648 self.queue = queue.Queue(-1)
2649 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2650 self.que_logger = logging.getLogger('que')
2651 self.que_logger.propagate = False
2652 self.que_logger.setLevel(logging.WARNING)
2653 self.que_logger.addHandler(self.que_hdlr)
2654
2655 def tearDown(self):
2656 self.que_hdlr.close()
2657 BaseTest.tearDown(self)
2658
2659 def test_queue_handler(self):
2660 self.que_logger.debug(self.next_message())
2661 self.assertRaises(queue.Empty, self.queue.get_nowait)
2662 self.que_logger.info(self.next_message())
2663 self.assertRaises(queue.Empty, self.queue.get_nowait)
2664 msg = self.next_message()
2665 self.que_logger.warning(msg)
2666 data = self.queue.get_nowait()
2667 self.assertTrue(isinstance(data, logging.LogRecord))
2668 self.assertEqual(data.name, self.que_logger.name)
2669 self.assertEqual((data.msg, data.args), (msg, None))
2670
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002671 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2672 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002673 def test_queue_listener(self):
2674 handler = TestHandler(Matcher())
2675 listener = logging.handlers.QueueListener(self.queue, handler)
2676 listener.start()
2677 try:
2678 self.que_logger.warning(self.next_message())
2679 self.que_logger.error(self.next_message())
2680 self.que_logger.critical(self.next_message())
2681 finally:
2682 listener.stop()
2683 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2684 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2685 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2686
Vinay Sajip37eb3382011-04-26 20:26:41 +01002687ZERO = datetime.timedelta(0)
2688
2689class UTC(datetime.tzinfo):
2690 def utcoffset(self, dt):
2691 return ZERO
2692
2693 dst = utcoffset
2694
2695 def tzname(self, dt):
2696 return 'UTC'
2697
2698utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002699
Vinay Sajipa39c5712010-10-25 13:57:39 +00002700class FormatterTest(unittest.TestCase):
2701 def setUp(self):
2702 self.common = {
2703 'name': 'formatter.test',
2704 'level': logging.DEBUG,
2705 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2706 'lineno': 42,
2707 'exc_info': None,
2708 'func': None,
2709 'msg': 'Message with %d %s',
2710 'args': (2, 'placeholders'),
2711 }
2712 self.variants = {
2713 }
2714
2715 def get_record(self, name=None):
2716 result = dict(self.common)
2717 if name is not None:
2718 result.update(self.variants[name])
2719 return logging.makeLogRecord(result)
2720
2721 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002722 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002723 r = self.get_record()
2724 f = logging.Formatter('${%(message)s}')
2725 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2726 f = logging.Formatter('%(random)s')
2727 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002728 self.assertFalse(f.usesTime())
2729 f = logging.Formatter('%(asctime)s')
2730 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002731 f = logging.Formatter('%(asctime)-15s')
2732 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002733 f = logging.Formatter('asctime')
2734 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002735
2736 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002737 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002738 r = self.get_record()
2739 f = logging.Formatter('$%{message}%$', style='{')
2740 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2741 f = logging.Formatter('{random}', style='{')
2742 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002743 self.assertFalse(f.usesTime())
2744 f = logging.Formatter('{asctime}', style='{')
2745 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002746 f = logging.Formatter('{asctime!s:15}', style='{')
2747 self.assertTrue(f.usesTime())
2748 f = logging.Formatter('{asctime:15}', style='{')
2749 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002750 f = logging.Formatter('asctime', style='{')
2751 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002752
2753 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002754 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002755 r = self.get_record()
2756 f = logging.Formatter('$message', style='$')
2757 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2758 f = logging.Formatter('$$%${message}%$$', style='$')
2759 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2760 f = logging.Formatter('${random}', style='$')
2761 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002762 self.assertFalse(f.usesTime())
2763 f = logging.Formatter('${asctime}', style='$')
2764 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002765 f = logging.Formatter('${asctime', style='$')
2766 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002767 f = logging.Formatter('$asctime', style='$')
2768 self.assertTrue(f.usesTime())
2769 f = logging.Formatter('asctime', style='$')
2770 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002771
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002772 def test_invalid_style(self):
2773 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2774
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002775 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002776 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002777 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002778 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002779 r.msecs = 123
2780 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002781 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002782 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2783 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002784 f.format(r)
2785 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2786
2787class TestBufferingFormatter(logging.BufferingFormatter):
2788 def formatHeader(self, records):
2789 return '[(%d)' % len(records)
2790
2791 def formatFooter(self, records):
2792 return '(%d)]' % len(records)
2793
2794class BufferingFormatterTest(unittest.TestCase):
2795 def setUp(self):
2796 self.records = [
2797 logging.makeLogRecord({'msg': 'one'}),
2798 logging.makeLogRecord({'msg': 'two'}),
2799 ]
2800
2801 def test_default(self):
2802 f = logging.BufferingFormatter()
2803 self.assertEqual('', f.format([]))
2804 self.assertEqual('onetwo', f.format(self.records))
2805
2806 def test_custom(self):
2807 f = TestBufferingFormatter()
2808 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2809 lf = logging.Formatter('<%(message)s>')
2810 f = TestBufferingFormatter(lf)
2811 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002812
2813class ExceptionTest(BaseTest):
2814 def test_formatting(self):
2815 r = self.root_logger
2816 h = RecordingHandler()
2817 r.addHandler(h)
2818 try:
2819 raise RuntimeError('deliberate mistake')
2820 except:
2821 logging.exception('failed', stack_info=True)
2822 r.removeHandler(h)
2823 h.close()
2824 r = h.records[0]
2825 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2826 'call last):\n'))
2827 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2828 'deliberate mistake'))
2829 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2830 'call last):\n'))
2831 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2832 'stack_info=True)'))
2833
2834
Vinay Sajip5a27d402010-12-10 11:42:57 +00002835class LastResortTest(BaseTest):
2836 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002837 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002838 root = self.root_logger
2839 root.removeHandler(self.root_hdlr)
2840 old_stderr = sys.stderr
2841 old_lastresort = logging.lastResort
2842 old_raise_exceptions = logging.raiseExceptions
2843 try:
2844 sys.stderr = sio = io.StringIO()
2845 root.warning('This is your final chance!')
2846 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2847 #No handlers and no last resort, so 'No handlers' message
2848 logging.lastResort = None
2849 sys.stderr = sio = io.StringIO()
2850 root.warning('This is your final chance!')
2851 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2852 # 'No handlers' message only printed once
2853 sys.stderr = sio = io.StringIO()
2854 root.warning('This is your final chance!')
2855 self.assertEqual(sio.getvalue(), '')
2856 root.manager.emittedNoHandlerWarning = False
2857 #If raiseExceptions is False, no message is printed
2858 logging.raiseExceptions = False
2859 sys.stderr = sio = io.StringIO()
2860 root.warning('This is your final chance!')
2861 self.assertEqual(sio.getvalue(), '')
2862 finally:
2863 sys.stderr = old_stderr
2864 root.addHandler(self.root_hdlr)
2865 logging.lastResort = old_lastresort
2866 logging.raiseExceptions = old_raise_exceptions
2867
2868
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002869class FakeHandler:
2870
2871 def __init__(self, identifier, called):
2872 for method in ('acquire', 'flush', 'close', 'release'):
2873 setattr(self, method, self.record_call(identifier, method, called))
2874
2875 def record_call(self, identifier, method_name, called):
2876 def inner():
2877 called.append('{} - {}'.format(identifier, method_name))
2878 return inner
2879
2880
2881class RecordingHandler(logging.NullHandler):
2882
2883 def __init__(self, *args, **kwargs):
2884 super(RecordingHandler, self).__init__(*args, **kwargs)
2885 self.records = []
2886
2887 def handle(self, record):
2888 """Keep track of all the emitted records."""
2889 self.records.append(record)
2890
2891
2892class ShutdownTest(BaseTest):
2893
Vinay Sajip5e66b162011-04-20 15:41:14 +01002894 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002895
2896 def setUp(self):
2897 super(ShutdownTest, self).setUp()
2898 self.called = []
2899
2900 raise_exceptions = logging.raiseExceptions
2901 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2902
2903 def raise_error(self, error):
2904 def inner():
2905 raise error()
2906 return inner
2907
2908 def test_no_failure(self):
2909 # create some fake handlers
2910 handler0 = FakeHandler(0, self.called)
2911 handler1 = FakeHandler(1, self.called)
2912 handler2 = FakeHandler(2, self.called)
2913
2914 # create live weakref to those handlers
2915 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2916
2917 logging.shutdown(handlerList=list(handlers))
2918
2919 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2920 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2921 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2922 self.assertEqual(expected, self.called)
2923
2924 def _test_with_failure_in_method(self, method, error):
2925 handler = FakeHandler(0, self.called)
2926 setattr(handler, method, self.raise_error(error))
2927 handlers = [logging.weakref.ref(handler)]
2928
2929 logging.shutdown(handlerList=list(handlers))
2930
2931 self.assertEqual('0 - release', self.called[-1])
2932
2933 def test_with_ioerror_in_acquire(self):
2934 self._test_with_failure_in_method('acquire', IOError)
2935
2936 def test_with_ioerror_in_flush(self):
2937 self._test_with_failure_in_method('flush', IOError)
2938
2939 def test_with_ioerror_in_close(self):
2940 self._test_with_failure_in_method('close', IOError)
2941
2942 def test_with_valueerror_in_acquire(self):
2943 self._test_with_failure_in_method('acquire', ValueError)
2944
2945 def test_with_valueerror_in_flush(self):
2946 self._test_with_failure_in_method('flush', ValueError)
2947
2948 def test_with_valueerror_in_close(self):
2949 self._test_with_failure_in_method('close', ValueError)
2950
2951 def test_with_other_error_in_acquire_without_raise(self):
2952 logging.raiseExceptions = False
2953 self._test_with_failure_in_method('acquire', IndexError)
2954
2955 def test_with_other_error_in_flush_without_raise(self):
2956 logging.raiseExceptions = False
2957 self._test_with_failure_in_method('flush', IndexError)
2958
2959 def test_with_other_error_in_close_without_raise(self):
2960 logging.raiseExceptions = False
2961 self._test_with_failure_in_method('close', IndexError)
2962
2963 def test_with_other_error_in_acquire_with_raise(self):
2964 logging.raiseExceptions = True
2965 self.assertRaises(IndexError, self._test_with_failure_in_method,
2966 'acquire', IndexError)
2967
2968 def test_with_other_error_in_flush_with_raise(self):
2969 logging.raiseExceptions = True
2970 self.assertRaises(IndexError, self._test_with_failure_in_method,
2971 'flush', IndexError)
2972
2973 def test_with_other_error_in_close_with_raise(self):
2974 logging.raiseExceptions = True
2975 self.assertRaises(IndexError, self._test_with_failure_in_method,
2976 'close', IndexError)
2977
2978
2979class ModuleLevelMiscTest(BaseTest):
2980
Vinay Sajip5e66b162011-04-20 15:41:14 +01002981 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002982
2983 def test_disable(self):
2984 old_disable = logging.root.manager.disable
2985 # confirm our assumptions are correct
2986 assert old_disable == 0
2987 self.addCleanup(lambda: logging.disable(old_disable))
2988
2989 logging.disable(83)
2990 self.assertEqual(logging.root.manager.disable, 83)
2991
2992 def _test_log(self, method, level=None):
2993 called = []
2994 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002995 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002996
2997 recording = RecordingHandler()
2998 logging.root.addHandler(recording)
2999
3000 log_method = getattr(logging, method)
3001 if level is not None:
3002 log_method(level, "test me: %r", recording)
3003 else:
3004 log_method("test me: %r", recording)
3005
3006 self.assertEqual(len(recording.records), 1)
3007 record = recording.records[0]
3008 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3009
3010 expected_level = level if level is not None else getattr(logging, method.upper())
3011 self.assertEqual(record.levelno, expected_level)
3012
3013 # basicConfig was not called!
3014 self.assertEqual(called, [])
3015
3016 def test_log(self):
3017 self._test_log('log', logging.ERROR)
3018
3019 def test_debug(self):
3020 self._test_log('debug')
3021
3022 def test_info(self):
3023 self._test_log('info')
3024
3025 def test_warning(self):
3026 self._test_log('warning')
3027
3028 def test_error(self):
3029 self._test_log('error')
3030
3031 def test_critical(self):
3032 self._test_log('critical')
3033
3034 def test_set_logger_class(self):
3035 self.assertRaises(TypeError, logging.setLoggerClass, object)
3036
3037 class MyLogger(logging.Logger):
3038 pass
3039
3040 logging.setLoggerClass(MyLogger)
3041 self.assertEqual(logging.getLoggerClass(), MyLogger)
3042
3043 logging.setLoggerClass(logging.Logger)
3044 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3045
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003046class LogRecordTest(BaseTest):
3047 def test_str_rep(self):
3048 r = logging.makeLogRecord({})
3049 s = str(r)
3050 self.assertTrue(s.startswith('<LogRecord: '))
3051 self.assertTrue(s.endswith('>'))
3052
3053 def test_dict_arg(self):
3054 h = RecordingHandler()
3055 r = logging.getLogger()
3056 r.addHandler(h)
3057 d = {'less' : 'more' }
3058 logging.warning('less is %(less)s', d)
3059 self.assertIs(h.records[0].args, d)
3060 self.assertEqual(h.records[0].message, 'less is more')
3061 r.removeHandler(h)
3062 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003063
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003064 def test_multiprocessing(self):
3065 r = logging.makeLogRecord({})
3066 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003067 try:
3068 import multiprocessing as mp
3069 r = logging.makeLogRecord({})
3070 self.assertEqual(r.processName, mp.current_process().name)
3071 except ImportError:
3072 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003073
3074 def test_optional(self):
3075 r = logging.makeLogRecord({})
3076 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003077 if threading:
3078 NOT_NONE(r.thread)
3079 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003080 NOT_NONE(r.process)
3081 NOT_NONE(r.processName)
3082 log_threads = logging.logThreads
3083 log_processes = logging.logProcesses
3084 log_multiprocessing = logging.logMultiprocessing
3085 try:
3086 logging.logThreads = False
3087 logging.logProcesses = False
3088 logging.logMultiprocessing = False
3089 r = logging.makeLogRecord({})
3090 NONE = self.assertIsNone
3091 NONE(r.thread)
3092 NONE(r.threadName)
3093 NONE(r.process)
3094 NONE(r.processName)
3095 finally:
3096 logging.logThreads = log_threads
3097 logging.logProcesses = log_processes
3098 logging.logMultiprocessing = log_multiprocessing
3099
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003100class BasicConfigTest(unittest.TestCase):
3101
Vinay Sajip95bf5042011-04-20 11:50:56 +01003102 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003103
3104 def setUp(self):
3105 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003106 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003107 self.saved_handlers = logging._handlers.copy()
3108 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003109 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003110 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003111 logging.root.handlers = []
3112
3113 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003114 for h in logging.root.handlers[:]:
3115 logging.root.removeHandler(h)
3116 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003117 super(BasicConfigTest, self).tearDown()
3118
Vinay Sajip3def7e02011-04-20 10:58:06 +01003119 def cleanup(self):
3120 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003121 logging._handlers.clear()
3122 logging._handlers.update(self.saved_handlers)
3123 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003124 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003125
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003126 def test_no_kwargs(self):
3127 logging.basicConfig()
3128
3129 # handler defaults to a StreamHandler to sys.stderr
3130 self.assertEqual(len(logging.root.handlers), 1)
3131 handler = logging.root.handlers[0]
3132 self.assertIsInstance(handler, logging.StreamHandler)
3133 self.assertEqual(handler.stream, sys.stderr)
3134
3135 formatter = handler.formatter
3136 # format defaults to logging.BASIC_FORMAT
3137 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3138 # datefmt defaults to None
3139 self.assertIsNone(formatter.datefmt)
3140 # style defaults to %
3141 self.assertIsInstance(formatter._style, logging.PercentStyle)
3142
3143 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003144 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003145
3146 def test_filename(self):
3147 logging.basicConfig(filename='test.log')
3148
3149 self.assertEqual(len(logging.root.handlers), 1)
3150 handler = logging.root.handlers[0]
3151 self.assertIsInstance(handler, logging.FileHandler)
3152
3153 expected = logging.FileHandler('test.log', 'a')
3154 self.addCleanup(expected.close)
3155 self.assertEqual(handler.stream.mode, expected.stream.mode)
3156 self.assertEqual(handler.stream.name, expected.stream.name)
3157
3158 def test_filemode(self):
3159 logging.basicConfig(filename='test.log', filemode='wb')
3160
3161 handler = logging.root.handlers[0]
3162 expected = logging.FileHandler('test.log', 'wb')
3163 self.addCleanup(expected.close)
3164 self.assertEqual(handler.stream.mode, expected.stream.mode)
3165
3166 def test_stream(self):
3167 stream = io.StringIO()
3168 self.addCleanup(stream.close)
3169 logging.basicConfig(stream=stream)
3170
3171 self.assertEqual(len(logging.root.handlers), 1)
3172 handler = logging.root.handlers[0]
3173 self.assertIsInstance(handler, logging.StreamHandler)
3174 self.assertEqual(handler.stream, stream)
3175
3176 def test_format(self):
3177 logging.basicConfig(format='foo')
3178
3179 formatter = logging.root.handlers[0].formatter
3180 self.assertEqual(formatter._style._fmt, 'foo')
3181
3182 def test_datefmt(self):
3183 logging.basicConfig(datefmt='bar')
3184
3185 formatter = logging.root.handlers[0].formatter
3186 self.assertEqual(formatter.datefmt, 'bar')
3187
3188 def test_style(self):
3189 logging.basicConfig(style='$')
3190
3191 formatter = logging.root.handlers[0].formatter
3192 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3193
3194 def test_level(self):
3195 old_level = logging.root.level
3196 self.addCleanup(lambda: logging.root.setLevel(old_level))
3197
3198 logging.basicConfig(level=57)
3199 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003200 # Test that second call has no effect
3201 logging.basicConfig(level=58)
3202 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003203
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003204 def test_incompatible(self):
3205 assertRaises = self.assertRaises
3206 handlers = [logging.StreamHandler()]
3207 stream = sys.stderr
3208 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3209 stream=stream)
3210 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3211 handlers=handlers)
3212 assertRaises(ValueError, logging.basicConfig, stream=stream,
3213 handlers=handlers)
3214
3215 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003216 handlers = [
3217 logging.StreamHandler(),
3218 logging.StreamHandler(sys.stdout),
3219 logging.StreamHandler(),
3220 ]
3221 f = logging.Formatter()
3222 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003223 logging.basicConfig(handlers=handlers)
3224 self.assertIs(handlers[0], logging.root.handlers[0])
3225 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003226 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003227 self.assertIsNotNone(handlers[0].formatter)
3228 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003229 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003230 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3231
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003232 def _test_log(self, method, level=None):
3233 # logging.root has no handlers so basicConfig should be called
3234 called = []
3235
3236 old_basic_config = logging.basicConfig
3237 def my_basic_config(*a, **kw):
3238 old_basic_config()
3239 old_level = logging.root.level
3240 logging.root.setLevel(100) # avoid having messages in stderr
3241 self.addCleanup(lambda: logging.root.setLevel(old_level))
3242 called.append((a, kw))
3243
3244 patch(self, logging, 'basicConfig', my_basic_config)
3245
3246 log_method = getattr(logging, method)
3247 if level is not None:
3248 log_method(level, "test me")
3249 else:
3250 log_method("test me")
3251
3252 # basicConfig was called with no arguments
3253 self.assertEqual(called, [((), {})])
3254
3255 def test_log(self):
3256 self._test_log('log', logging.WARNING)
3257
3258 def test_debug(self):
3259 self._test_log('debug')
3260
3261 def test_info(self):
3262 self._test_log('info')
3263
3264 def test_warning(self):
3265 self._test_log('warning')
3266
3267 def test_error(self):
3268 self._test_log('error')
3269
3270 def test_critical(self):
3271 self._test_log('critical')
3272
3273
3274class LoggerAdapterTest(unittest.TestCase):
3275
3276 def setUp(self):
3277 super(LoggerAdapterTest, self).setUp()
3278 old_handler_list = logging._handlerList[:]
3279
3280 self.recording = RecordingHandler()
3281 self.logger = logging.root
3282 self.logger.addHandler(self.recording)
3283 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3284 self.addCleanup(self.recording.close)
3285
3286 def cleanup():
3287 logging._handlerList[:] = old_handler_list
3288
3289 self.addCleanup(cleanup)
3290 self.addCleanup(logging.shutdown)
3291 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3292
3293 def test_exception(self):
3294 msg = 'testing exception: %r'
3295 exc = None
3296 try:
3297 assert False
3298 except AssertionError as e:
3299 exc = e
3300 self.adapter.exception(msg, self.recording)
3301
3302 self.assertEqual(len(self.recording.records), 1)
3303 record = self.recording.records[0]
3304 self.assertEqual(record.levelno, logging.ERROR)
3305 self.assertEqual(record.msg, msg)
3306 self.assertEqual(record.args, (self.recording,))
3307 self.assertEqual(record.exc_info,
3308 (exc.__class__, exc, exc.__traceback__))
3309
3310 def test_critical(self):
3311 msg = 'critical test! %r'
3312 self.adapter.critical(msg, self.recording)
3313
3314 self.assertEqual(len(self.recording.records), 1)
3315 record = self.recording.records[0]
3316 self.assertEqual(record.levelno, logging.CRITICAL)
3317 self.assertEqual(record.msg, msg)
3318 self.assertEqual(record.args, (self.recording,))
3319
3320 def test_is_enabled_for(self):
3321 old_disable = self.adapter.logger.manager.disable
3322 self.adapter.logger.manager.disable = 33
3323 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3324 'disable', old_disable))
3325 self.assertFalse(self.adapter.isEnabledFor(32))
3326
3327 def test_has_handlers(self):
3328 self.assertTrue(self.adapter.hasHandlers())
3329
3330 for handler in self.logger.handlers:
3331 self.logger.removeHandler(handler)
3332 assert not self.logger.hasHandlers()
3333
3334 self.assertFalse(self.adapter.hasHandlers())
3335
3336
3337class LoggerTest(BaseTest):
3338
3339 def setUp(self):
3340 super(LoggerTest, self).setUp()
3341 self.recording = RecordingHandler()
3342 self.logger = logging.Logger(name='blah')
3343 self.logger.addHandler(self.recording)
3344 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3345 self.addCleanup(self.recording.close)
3346 self.addCleanup(logging.shutdown)
3347
3348 def test_set_invalid_level(self):
3349 self.assertRaises(TypeError, self.logger.setLevel, object())
3350
3351 def test_exception(self):
3352 msg = 'testing exception: %r'
3353 exc = None
3354 try:
3355 assert False
3356 except AssertionError as e:
3357 exc = e
3358 self.logger.exception(msg, self.recording)
3359
3360 self.assertEqual(len(self.recording.records), 1)
3361 record = self.recording.records[0]
3362 self.assertEqual(record.levelno, logging.ERROR)
3363 self.assertEqual(record.msg, msg)
3364 self.assertEqual(record.args, (self.recording,))
3365 self.assertEqual(record.exc_info,
3366 (exc.__class__, exc, exc.__traceback__))
3367
3368 def test_log_invalid_level_with_raise(self):
3369 old_raise = logging.raiseExceptions
3370 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3371
3372 logging.raiseExceptions = True
3373 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3374
3375 def test_log_invalid_level_no_raise(self):
3376 old_raise = logging.raiseExceptions
3377 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3378
3379 logging.raiseExceptions = False
3380 self.logger.log('10', 'test message') # no exception happens
3381
3382 def test_find_caller_with_stack_info(self):
3383 called = []
3384 patch(self, logging.traceback, 'print_stack',
3385 lambda f, file: called.append(file.getvalue()))
3386
3387 self.logger.findCaller(stack_info=True)
3388
3389 self.assertEqual(len(called), 1)
3390 self.assertEqual('Stack (most recent call last):\n', called[0])
3391
3392 def test_make_record_with_extra_overwrite(self):
3393 name = 'my record'
3394 level = 13
3395 fn = lno = msg = args = exc_info = func = sinfo = None
3396 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3397 exc_info, func, sinfo)
3398
3399 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3400 extra = {key: 'some value'}
3401 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3402 fn, lno, msg, args, exc_info,
3403 extra=extra, sinfo=sinfo)
3404
3405 def test_make_record_with_extra_no_overwrite(self):
3406 name = 'my record'
3407 level = 13
3408 fn = lno = msg = args = exc_info = func = sinfo = None
3409 extra = {'valid_key': 'some value'}
3410 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3411 exc_info, extra=extra, sinfo=sinfo)
3412 self.assertIn('valid_key', result.__dict__)
3413
3414 def test_has_handlers(self):
3415 self.assertTrue(self.logger.hasHandlers())
3416
3417 for handler in self.logger.handlers:
3418 self.logger.removeHandler(handler)
3419 assert not self.logger.hasHandlers()
3420
3421 self.assertFalse(self.logger.hasHandlers())
3422
3423 def test_has_handlers_no_propagate(self):
3424 child_logger = logging.getLogger('blah.child')
3425 child_logger.propagate = False
3426 assert child_logger.handlers == []
3427
3428 self.assertFalse(child_logger.hasHandlers())
3429
3430 def test_is_enabled_for(self):
3431 old_disable = self.logger.manager.disable
3432 self.logger.manager.disable = 23
3433 self.addCleanup(lambda: setattr(self.logger.manager,
3434 'disable', old_disable))
3435 self.assertFalse(self.logger.isEnabledFor(22))
3436
3437
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003438class BaseFileTest(BaseTest):
3439 "Base class for handler tests that write log files"
3440
3441 def setUp(self):
3442 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003443 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3444 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003445 self.rmfiles = []
3446
3447 def tearDown(self):
3448 for fn in self.rmfiles:
3449 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003450 if os.path.exists(self.fn):
3451 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003452 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003453
3454 def assertLogFile(self, filename):
3455 "Assert a log file is there and register it for deletion"
3456 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003457 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003458 self.rmfiles.append(filename)
3459
3460
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003461class FileHandlerTest(BaseFileTest):
3462 def test_delay(self):
3463 os.unlink(self.fn)
3464 fh = logging.FileHandler(self.fn, delay=True)
3465 self.assertIsNone(fh.stream)
3466 self.assertFalse(os.path.exists(self.fn))
3467 fh.handle(logging.makeLogRecord({}))
3468 self.assertIsNotNone(fh.stream)
3469 self.assertTrue(os.path.exists(self.fn))
3470 fh.close()
3471
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003472class RotatingFileHandlerTest(BaseFileTest):
3473 def next_rec(self):
3474 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3475 self.next_message(), None, None, None)
3476
3477 def test_should_not_rollover(self):
3478 # If maxbytes is zero rollover never occurs
3479 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3480 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003481 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003482
3483 def test_should_rollover(self):
3484 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3485 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003486 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003487
3488 def test_file_created(self):
3489 # checks that the file is created and assumes it was created
3490 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003491 rh = logging.handlers.RotatingFileHandler(self.fn)
3492 rh.emit(self.next_rec())
3493 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003494 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003495
3496 def test_rollover_filenames(self):
3497 rh = logging.handlers.RotatingFileHandler(
3498 self.fn, backupCount=2, maxBytes=1)
3499 rh.emit(self.next_rec())
3500 self.assertLogFile(self.fn)
3501 rh.emit(self.next_rec())
3502 self.assertLogFile(self.fn + ".1")
3503 rh.emit(self.next_rec())
3504 self.assertLogFile(self.fn + ".2")
3505 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003506 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003507
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003508class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003509 # other test methods added below
3510 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003511 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3512 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003513 r = logging.makeLogRecord({'msg': 'testing'})
3514 fh.emit(r)
3515 self.assertLogFile(self.fn)
3516 time.sleep(1.0)
3517 fh.emit(r)
3518 fh.close()
3519 # At this point, we should have a recent rotated file which we
3520 # can test for the existence of. However, in practice, on some
3521 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003522 # in time to go to look for the log file. So, we go back a fair
3523 # bit, and stop as soon as we see a rotated file. In theory this
3524 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003525 found = False
3526 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003527 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003528 for secs in range(GO_BACK):
3529 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003530 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3531 found = os.path.exists(fn)
3532 if found:
3533 self.rmfiles.append(fn)
3534 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003535 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3536 if not found:
3537 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003538 dn, fn = os.path.split(self.fn)
3539 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Vinay Sajipa552ca62011-05-13 12:16:04 +01003540 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"))
Vinay Sajip672c5812011-05-13 07:09:40 +01003541 print('The only matching files are: %s' % files)
Vinay Sajipfd285022011-05-07 17:01:22 +01003542 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003543
Vinay Sajip0372e102011-05-05 12:59:14 +01003544 def test_invalid(self):
3545 assertRaises = self.assertRaises
3546 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003547 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003548 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003549 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003550 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003551 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003552
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003553def secs(**kw):
3554 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3555
3556for when, exp in (('S', 1),
3557 ('M', 60),
3558 ('H', 60 * 60),
3559 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003560 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003561 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003562 ('W0', secs(days=4, hours=24)),
3563 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003564 def test_compute_rollover(self, when=when, exp=exp):
3565 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003566 self.fn, when=when, interval=1, backupCount=0, utc=True)
3567 currentTime = 0.0
3568 actual = rh.computeRollover(currentTime)
3569 if exp != actual:
3570 # Failures occur on some systems for MIDNIGHT and W0.
3571 # Print detailed calculation for MIDNIGHT so we can try to see
3572 # what's going on
3573 import time
3574 if when == 'MIDNIGHT':
3575 try:
3576 if rh.utc:
3577 t = time.gmtime(currentTime)
3578 else:
3579 t = time.localtime(currentTime)
3580 currentHour = t[3]
3581 currentMinute = t[4]
3582 currentSecond = t[5]
3583 # r is the number of seconds left between now and midnight
3584 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3585 currentMinute) * 60 +
3586 currentSecond)
3587 result = currentTime + r
3588 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3589 print('currentHour: %s' % currentHour, file=sys.stderr)
3590 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3591 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3592 print('r: %s' % r, file=sys.stderr)
3593 print('result: %s' % result, file=sys.stderr)
3594 except Exception:
3595 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3596 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003597 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003598 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3599
Vinay Sajip60ccd822011-05-09 17:32:09 +01003600
3601@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3602class NTEventLogHandlerTest(BaseTest):
3603 def test_basic(self):
3604 logtype = 'Application'
3605 elh = win32evtlog.OpenEventLog(None, logtype)
3606 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3607 h = logging.handlers.NTEventLogHandler('test_logging')
3608 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3609 h.handle(r)
3610 h.close()
3611 # Now see if the event is recorded
3612 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3613 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3614 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3615 found = False
3616 GO_BACK = 100
3617 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3618 for e in events:
3619 if e.SourceName != 'test_logging':
3620 continue
3621 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3622 if msg != 'Test Log Message\r\n':
3623 continue
3624 found = True
3625 break
3626 msg = 'Record not found in event log, went back %d records' % GO_BACK
3627 self.assertTrue(found, msg=msg)
3628
Christian Heimes180510d2008-03-03 19:15:45 +00003629# Set the locale to the platform-dependent default. I have no idea
3630# why the test does this, but in any case we save the current locale
3631# first and restore it at the end.
3632@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003633def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003634 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003635 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003636 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3637 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3638 ManagerTest, FormatterTest, BufferingFormatterTest,
3639 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3640 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3641 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3642 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003643 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003644 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003645 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003646 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003647
Christian Heimes180510d2008-03-03 19:15:45 +00003648if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003649 test_main()