blob: 6c66ff109ccc25f4b33bc295d4d23c7064229a82 [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 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100813 def __init__(self, addr, handler, poll_interval=0.5,
814 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100815 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
816 def __getattr__(self, name, default=None):
817 if name.startswith('do_'):
818 return self.process_request
819 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100820
Vinay Sajipce7c9782011-05-17 07:15:53 +0100821 def process_request(self):
822 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100823
Vinay Sajipce7c9782011-05-17 07:15:53 +0100824 def log_message(self, format, *args):
825 if log:
826 super(DelegatingHTTPRequestHandler,
827 self).log_message(format, *args)
828 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
829 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100830 self.sslctx = sslctx
831
832 def get_request(self):
833 try:
834 sock, addr = self.socket.accept()
835 if self.sslctx:
836 sock = self.sslctx.wrap_socket(sock, server_side=True)
837 except socket.error as e:
838 # socket errors are silenced by the caller, print them here
839 sys.stderr.write("Got an error:\n%s\n" % e)
840 raise
841 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100842
Vinay Sajipce7c9782011-05-17 07:15:53 +0100843 class TestTCPServer(ControlMixin, ThreadingTCPServer):
844 """
845 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100846
Vinay Sajipce7c9782011-05-17 07:15:53 +0100847 :param addr: A tuple with the IP address and port to listen on.
848 :param handler: A handler callable which will be called with a single
849 parameter - the request - in order to process the request.
850 :param poll_interval: The polling interval in seconds.
851 :bind_and_activate: If True (the default), binds the server and starts it
852 listening. If False, you need to call
853 :meth:`server_bind` and :meth:`server_activate` at
854 some later time before calling :meth:`start`, so that
855 the server will set up the socket and listen on it.
856 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100857
Vinay Sajipce7c9782011-05-17 07:15:53 +0100858 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100859
Vinay Sajipce7c9782011-05-17 07:15:53 +0100860 def __init__(self, addr, handler, poll_interval=0.5,
861 bind_and_activate=True):
862 class DelegatingTCPRequestHandler(StreamRequestHandler):
863
864 def handle(self):
865 self.server._handler(self)
866 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
867 bind_and_activate)
868 ControlMixin.__init__(self, handler, poll_interval)
869
870 def server_bind(self):
871 super(TestTCPServer, self).server_bind()
872 self.port = self.socket.getsockname()[1]
873
874 class TestUDPServer(ControlMixin, ThreadingUDPServer):
875 """
876 A UDP server which is controllable using :class:`ControlMixin`.
877
878 :param addr: A tuple with the IP address and port to listen on.
879 :param handler: A handler callable which will be called with a
880 single parameter - the request - in order to
881 process the request.
882 :param poll_interval: The polling interval for shutdown requests,
883 in seconds.
884 :bind_and_activate: If True (the default), binds the server and
885 starts it listening. If False, you need to
886 call :meth:`server_bind` and
887 :meth:`server_activate` at some later time
888 before calling :meth:`start`, so that the server will
889 set up the socket and listen on it.
890 """
891 def __init__(self, addr, handler, poll_interval=0.5, bind_and_activate=True):
892 class DelegatingUDPRequestHandler(DatagramRequestHandler):
893
894 def handle(self):
895 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100896
897 def finish(self):
898 data = self.wfile.getvalue()
899 try:
900 super(DelegatingUDPRequestHandler, self).finish()
901 except socket.error:
902 msg = ('Error during finish, while sending %r, '
903 'closed = %s')
904 print(msg % (data, self._closed), file=sys.stderr)
905 raise
906
Vinay Sajipce7c9782011-05-17 07:15:53 +0100907 ThreadingUDPServer.__init__(self, addr, DelegatingUDPRequestHandler,
908 bind_and_activate)
909 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100910 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100911
912 def server_bind(self):
913 super(TestUDPServer, self).server_bind()
914 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100915
Vinay Sajipba980db2011-05-23 21:37:54 +0100916 def server_close(self):
917 super(TestUDPServer, self).server_close()
918 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100919
920# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100921
Vinay Sajipce7c9782011-05-17 07:15:53 +0100922@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100923class SMTPHandlerTest(BaseTest):
924 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100925 sockmap = {}
926 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
927 sockmap)
928 server.start()
929 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100930 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
931 self.assertEqual(h.toaddrs, ['you'])
932 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100933 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100934 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100935 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100936 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100937 server.stop()
938 self.assertEqual(len(self.messages), 1)
939 peer, mailfrom, rcpttos, data = self.messages[0]
940 self.assertEqual(mailfrom, 'me')
941 self.assertEqual(rcpttos, ['you'])
942 self.assertTrue('\nSubject: Log\n' in data)
943 self.assertTrue(data.endswith('\n\nHello'))
944 h.close()
945
946 def process_message(self, *args):
947 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100948 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100949
Christian Heimes180510d2008-03-03 19:15:45 +0000950class MemoryHandlerTest(BaseTest):
951
952 """Tests for the MemoryHandler."""
953
954 # Do not bother with a logger name group.
955 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
956
957 def setUp(self):
958 BaseTest.setUp(self)
959 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
960 self.root_hdlr)
961 self.mem_logger = logging.getLogger('mem')
962 self.mem_logger.propagate = 0
963 self.mem_logger.addHandler(self.mem_hdlr)
964
965 def tearDown(self):
966 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000967 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000968
969 def test_flush(self):
970 # The memory handler flushes to its target handler based on specific
971 # criteria (message count and message level).
972 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000973 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000974 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000975 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000976 # This will flush because the level is >= logging.WARNING
977 self.mem_logger.warn(self.next_message())
978 lines = [
979 ('DEBUG', '1'),
980 ('INFO', '2'),
981 ('WARNING', '3'),
982 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000983 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000984 for n in (4, 14):
985 for i in range(9):
986 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000987 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000988 # This will flush because it's the 10th message since the last
989 # flush.
990 self.mem_logger.debug(self.next_message())
991 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000992 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000993
994 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000995 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000996
997
998class ExceptionFormatter(logging.Formatter):
999 """A special exception formatter."""
1000 def formatException(self, ei):
1001 return "Got a [%s]" % ei[0].__name__
1002
1003
1004class ConfigFileTest(BaseTest):
1005
1006 """Reading logging config from a .ini-style config file."""
1007
1008 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1009
1010 # config0 is a standard configuration.
1011 config0 = """
1012 [loggers]
1013 keys=root
1014
1015 [handlers]
1016 keys=hand1
1017
1018 [formatters]
1019 keys=form1
1020
1021 [logger_root]
1022 level=WARNING
1023 handlers=hand1
1024
1025 [handler_hand1]
1026 class=StreamHandler
1027 level=NOTSET
1028 formatter=form1
1029 args=(sys.stdout,)
1030
1031 [formatter_form1]
1032 format=%(levelname)s ++ %(message)s
1033 datefmt=
1034 """
1035
1036 # config1 adds a little to the standard configuration.
1037 config1 = """
1038 [loggers]
1039 keys=root,parser
1040
1041 [handlers]
1042 keys=hand1
1043
1044 [formatters]
1045 keys=form1
1046
1047 [logger_root]
1048 level=WARNING
1049 handlers=
1050
1051 [logger_parser]
1052 level=DEBUG
1053 handlers=hand1
1054 propagate=1
1055 qualname=compiler.parser
1056
1057 [handler_hand1]
1058 class=StreamHandler
1059 level=NOTSET
1060 formatter=form1
1061 args=(sys.stdout,)
1062
1063 [formatter_form1]
1064 format=%(levelname)s ++ %(message)s
1065 datefmt=
1066 """
1067
Vinay Sajip3f84b072011-03-07 17:49:33 +00001068 # config1a moves the handler to the root.
1069 config1a = """
1070 [loggers]
1071 keys=root,parser
1072
1073 [handlers]
1074 keys=hand1
1075
1076 [formatters]
1077 keys=form1
1078
1079 [logger_root]
1080 level=WARNING
1081 handlers=hand1
1082
1083 [logger_parser]
1084 level=DEBUG
1085 handlers=
1086 propagate=1
1087 qualname=compiler.parser
1088
1089 [handler_hand1]
1090 class=StreamHandler
1091 level=NOTSET
1092 formatter=form1
1093 args=(sys.stdout,)
1094
1095 [formatter_form1]
1096 format=%(levelname)s ++ %(message)s
1097 datefmt=
1098 """
1099
Christian Heimes180510d2008-03-03 19:15:45 +00001100 # config2 has a subtle configuration error that should be reported
1101 config2 = config1.replace("sys.stdout", "sys.stbout")
1102
1103 # config3 has a less subtle configuration error
1104 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1105
1106 # config4 specifies a custom formatter class to be loaded
1107 config4 = """
1108 [loggers]
1109 keys=root
1110
1111 [handlers]
1112 keys=hand1
1113
1114 [formatters]
1115 keys=form1
1116
1117 [logger_root]
1118 level=NOTSET
1119 handlers=hand1
1120
1121 [handler_hand1]
1122 class=StreamHandler
1123 level=NOTSET
1124 formatter=form1
1125 args=(sys.stdout,)
1126
1127 [formatter_form1]
1128 class=""" + __name__ + """.ExceptionFormatter
1129 format=%(levelname)s:%(name)s:%(message)s
1130 datefmt=
1131 """
1132
Georg Brandl3dbca812008-07-23 16:10:53 +00001133 # config5 specifies a custom handler class to be loaded
1134 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1135
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001136 # config6 uses ', ' delimiters in the handlers and formatters sections
1137 config6 = """
1138 [loggers]
1139 keys=root,parser
1140
1141 [handlers]
1142 keys=hand1, hand2
1143
1144 [formatters]
1145 keys=form1, form2
1146
1147 [logger_root]
1148 level=WARNING
1149 handlers=
1150
1151 [logger_parser]
1152 level=DEBUG
1153 handlers=hand1
1154 propagate=1
1155 qualname=compiler.parser
1156
1157 [handler_hand1]
1158 class=StreamHandler
1159 level=NOTSET
1160 formatter=form1
1161 args=(sys.stdout,)
1162
1163 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001164 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001165 level=NOTSET
1166 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001167 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001168
1169 [formatter_form1]
1170 format=%(levelname)s ++ %(message)s
1171 datefmt=
1172
1173 [formatter_form2]
1174 format=%(message)s
1175 datefmt=
1176 """
1177
Vinay Sajip3f84b072011-03-07 17:49:33 +00001178 # config7 adds a compiler logger.
1179 config7 = """
1180 [loggers]
1181 keys=root,parser,compiler
1182
1183 [handlers]
1184 keys=hand1
1185
1186 [formatters]
1187 keys=form1
1188
1189 [logger_root]
1190 level=WARNING
1191 handlers=hand1
1192
1193 [logger_compiler]
1194 level=DEBUG
1195 handlers=
1196 propagate=1
1197 qualname=compiler
1198
1199 [logger_parser]
1200 level=DEBUG
1201 handlers=
1202 propagate=1
1203 qualname=compiler.parser
1204
1205 [handler_hand1]
1206 class=StreamHandler
1207 level=NOTSET
1208 formatter=form1
1209 args=(sys.stdout,)
1210
1211 [formatter_form1]
1212 format=%(levelname)s ++ %(message)s
1213 datefmt=
1214 """
1215
Christian Heimes180510d2008-03-03 19:15:45 +00001216 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001217 file = io.StringIO(textwrap.dedent(conf))
1218 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001219
1220 def test_config0_ok(self):
1221 # A simple config file which overrides the default settings.
1222 with captured_stdout() as output:
1223 self.apply_config(self.config0)
1224 logger = logging.getLogger()
1225 # Won't output anything
1226 logger.info(self.next_message())
1227 # Outputs a message
1228 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001229 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001230 ('ERROR', '2'),
1231 ], stream=output)
1232 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001233 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001234
Georg Brandl3dbca812008-07-23 16:10:53 +00001235 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001236 # A config file defining a sub-parser as well.
1237 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001238 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001239 logger = logging.getLogger("compiler.parser")
1240 # Both will output a message
1241 logger.info(self.next_message())
1242 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001243 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001244 ('INFO', '1'),
1245 ('ERROR', '2'),
1246 ], stream=output)
1247 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001248 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001249
1250 def test_config2_failure(self):
1251 # A simple config file which overrides the default settings.
1252 self.assertRaises(Exception, self.apply_config, self.config2)
1253
1254 def test_config3_failure(self):
1255 # A simple config file which overrides the default settings.
1256 self.assertRaises(Exception, self.apply_config, self.config3)
1257
1258 def test_config4_ok(self):
1259 # A config file specifying a custom formatter class.
1260 with captured_stdout() as output:
1261 self.apply_config(self.config4)
1262 logger = logging.getLogger()
1263 try:
1264 raise RuntimeError()
1265 except RuntimeError:
1266 logging.exception("just testing")
1267 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001268 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001269 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1270 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001271 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001272
Georg Brandl3dbca812008-07-23 16:10:53 +00001273 def test_config5_ok(self):
1274 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001275
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001276 def test_config6_ok(self):
1277 self.test_config1_ok(config=self.config6)
1278
Vinay Sajip3f84b072011-03-07 17:49:33 +00001279 def test_config7_ok(self):
1280 with captured_stdout() as output:
1281 self.apply_config(self.config1a)
1282 logger = logging.getLogger("compiler.parser")
1283 # See issue #11424. compiler-hyphenated sorts
1284 # between compiler and compiler.xyz and this
1285 # was preventing compiler.xyz from being included
1286 # in the child loggers of compiler because of an
1287 # overzealous loop termination condition.
1288 hyphenated = logging.getLogger('compiler-hyphenated')
1289 # All will output a message
1290 logger.info(self.next_message())
1291 logger.error(self.next_message())
1292 hyphenated.critical(self.next_message())
1293 self.assert_log_lines([
1294 ('INFO', '1'),
1295 ('ERROR', '2'),
1296 ('CRITICAL', '3'),
1297 ], stream=output)
1298 # Original logger output is empty.
1299 self.assert_log_lines([])
1300 with captured_stdout() as output:
1301 self.apply_config(self.config7)
1302 logger = logging.getLogger("compiler.parser")
1303 self.assertFalse(logger.disabled)
1304 # Both will output a message
1305 logger.info(self.next_message())
1306 logger.error(self.next_message())
1307 logger = logging.getLogger("compiler.lexer")
1308 # Both will output a message
1309 logger.info(self.next_message())
1310 logger.error(self.next_message())
1311 # Will not appear
1312 hyphenated.critical(self.next_message())
1313 self.assert_log_lines([
1314 ('INFO', '4'),
1315 ('ERROR', '5'),
1316 ('INFO', '6'),
1317 ('ERROR', '7'),
1318 ], stream=output)
1319 # Original logger output is empty.
1320 self.assert_log_lines([])
1321
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001322
Victor Stinner45df8202010-04-28 22:31:17 +00001323@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001324class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001325
Christian Heimes180510d2008-03-03 19:15:45 +00001326 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001327
Christian Heimes180510d2008-03-03 19:15:45 +00001328 def setUp(self):
1329 """Set up a TCP server to receive log messages, and a SocketHandler
1330 pointing to that server's address and port."""
1331 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001332 addr = ('localhost', 0)
1333 self.server = server = TestTCPServer(addr, self.handle_socket,
1334 0.01)
1335 server.start()
1336 server.ready.wait()
1337 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1338 server.port)
1339 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001340 self.root_logger.removeHandler(self.root_logger.handlers[0])
1341 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001342 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001343
Christian Heimes180510d2008-03-03 19:15:45 +00001344 def tearDown(self):
1345 """Shutdown the TCP server."""
1346 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001347 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001348 self.root_logger.removeHandler(self.sock_hdlr)
1349 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001350 finally:
1351 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001352
Vinay Sajip7367d082011-05-02 13:17:27 +01001353 def handle_socket(self, request):
1354 conn = request.connection
1355 while True:
1356 chunk = conn.recv(4)
1357 if len(chunk) < 4:
1358 break
1359 slen = struct.unpack(">L", chunk)[0]
1360 chunk = conn.recv(slen)
1361 while len(chunk) < slen:
1362 chunk = chunk + conn.recv(slen - len(chunk))
1363 obj = pickle.loads(chunk)
1364 record = logging.makeLogRecord(obj)
1365 self.log_output += record.msg + '\n'
1366 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001367
Christian Heimes180510d2008-03-03 19:15:45 +00001368 def test_output(self):
1369 # The log message sent to the SocketHandler is properly received.
1370 logger = logging.getLogger("tcp")
1371 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001372 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001373 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001374 self.handled.acquire()
1375 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001376
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001377 def test_noserver(self):
1378 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001379 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001380 #The logging call should try to connect, which should fail
1381 try:
1382 raise RuntimeError('Deliberate mistake')
1383 except RuntimeError:
1384 self.root_logger.exception('Never sent')
1385 self.root_logger.error('Never sent, either')
1386 now = time.time()
1387 self.assertTrue(self.sock_hdlr.retryTime > now)
1388 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1389 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001390
Vinay Sajip7367d082011-05-02 13:17:27 +01001391
1392@unittest.skipUnless(threading, 'Threading required for this test.')
1393class DatagramHandlerTest(BaseTest):
1394
1395 """Test for DatagramHandler."""
1396
1397 def setUp(self):
1398 """Set up a UDP server to receive log messages, and a DatagramHandler
1399 pointing to that server's address and port."""
1400 BaseTest.setUp(self)
1401 addr = ('localhost', 0)
1402 self.server = server = TestUDPServer(addr, self.handle_datagram,
1403 0.01)
1404 server.start()
1405 server.ready.wait()
1406 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1407 server.port)
1408 self.log_output = ''
1409 self.root_logger.removeHandler(self.root_logger.handlers[0])
1410 self.root_logger.addHandler(self.sock_hdlr)
1411 self.handled = threading.Event()
1412
1413 def tearDown(self):
1414 """Shutdown the UDP server."""
1415 try:
1416 self.server.stop(2.0)
1417 self.root_logger.removeHandler(self.sock_hdlr)
1418 self.sock_hdlr.close()
1419 finally:
1420 BaseTest.tearDown(self)
1421
1422 def handle_datagram(self, request):
1423 slen = struct.pack('>L', 0) # length of prefix
1424 packet = request.packet[len(slen):]
1425 obj = pickle.loads(packet)
1426 record = logging.makeLogRecord(obj)
1427 self.log_output += record.msg + '\n'
1428 self.handled.set()
1429
1430 def test_output(self):
1431 # The log message sent to the DatagramHandler is properly received.
1432 logger = logging.getLogger("udp")
1433 logger.error("spam")
1434 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001435 self.handled.clear()
1436 logger.error("eggs")
1437 self.handled.wait()
1438 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001439
1440
1441@unittest.skipUnless(threading, 'Threading required for this test.')
1442class SysLogHandlerTest(BaseTest):
1443
1444 """Test for SysLogHandler using UDP."""
1445
1446 def setUp(self):
1447 """Set up a UDP server to receive log messages, and a SysLogHandler
1448 pointing to that server's address and port."""
1449 BaseTest.setUp(self)
1450 addr = ('localhost', 0)
1451 self.server = server = TestUDPServer(addr, self.handle_datagram,
1452 0.01)
1453 server.start()
1454 server.ready.wait()
1455 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1456 server.port))
1457 self.log_output = ''
1458 self.root_logger.removeHandler(self.root_logger.handlers[0])
1459 self.root_logger.addHandler(self.sl_hdlr)
1460 self.handled = threading.Event()
1461
1462 def tearDown(self):
1463 """Shutdown the UDP server."""
1464 try:
1465 self.server.stop(2.0)
1466 self.root_logger.removeHandler(self.sl_hdlr)
1467 self.sl_hdlr.close()
1468 finally:
1469 BaseTest.tearDown(self)
1470
1471 def handle_datagram(self, request):
1472 self.log_output = request.packet
1473 self.handled.set()
1474
1475 def test_output(self):
1476 # The log message sent to the SysLogHandler is properly received.
1477 logger = logging.getLogger("slh")
1478 logger.error("sp\xe4m")
1479 self.handled.wait()
1480 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1481
1482
1483@unittest.skipUnless(threading, 'Threading required for this test.')
1484class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001485 """Test for HTTPHandler."""
1486
Vinay Sajip9ba87612011-05-21 11:32:15 +01001487 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1488MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1489zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1490aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1491AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
14920tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
14935LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1494piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
14952z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1496i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1497PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1498E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1499rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1500/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1501-----END RSA PRIVATE KEY-----
1502-----BEGIN CERTIFICATE-----
1503MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1504BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1505EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1506xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1507l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1508vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1509BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1510S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1511BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15124f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15131c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1514IRbTpvlHWPjsSvHz0ZOH
1515-----END CERTIFICATE-----"""
1516
Vinay Sajip7367d082011-05-02 13:17:27 +01001517 def setUp(self):
1518 """Set up an HTTP server to receive log messages, and a HTTPHandler
1519 pointing to that server's address and port."""
1520 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001521 self.handled = threading.Event()
1522
Vinay Sajip7367d082011-05-02 13:17:27 +01001523 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001524 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001525 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001526 if self.command == 'POST':
1527 try:
1528 rlen = int(request.headers['Content-Length'])
1529 self.post_data = request.rfile.read(rlen)
1530 except:
1531 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001532 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001533 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001534 self.handled.set()
1535
1536 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001537 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001538 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001539 root_logger = self.root_logger
1540 root_logger.removeHandler(self.root_logger.handlers[0])
1541 for secure in (False, True):
1542 addr = ('localhost', 0)
1543 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001544 try:
1545 import ssl
1546 fd, fn = tempfile.mkstemp()
1547 os.close(fd)
1548 with open(fn, 'w') as f:
1549 f.write(self.PEMFILE)
1550 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1551 sslctx.load_cert_chain(fn)
1552 os.unlink(fn)
1553 except ImportError:
1554 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001555 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001556 sslctx = None
1557 self.server = server = TestHTTPServer(addr, self.handle_request,
1558 0.01, sslctx=sslctx)
1559 server.start()
1560 server.ready.wait()
1561 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001562 secure_client = secure and sslctx
1563 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1564 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001565 self.log_data = None
1566 root_logger.addHandler(self.h_hdlr)
1567
1568 for method in ('GET', 'POST'):
1569 self.h_hdlr.method = method
1570 self.handled.clear()
1571 msg = "sp\xe4m"
1572 logger.error(msg)
1573 self.handled.wait()
1574 self.assertEqual(self.log_data.path, '/frob')
1575 self.assertEqual(self.command, method)
1576 if method == 'GET':
1577 d = parse_qs(self.log_data.query)
1578 else:
1579 d = parse_qs(self.post_data.decode('utf-8'))
1580 self.assertEqual(d['name'], ['http'])
1581 self.assertEqual(d['funcName'], ['test_output'])
1582 self.assertEqual(d['msg'], [msg])
1583
1584 self.server.stop(2.0)
1585 self.root_logger.removeHandler(self.h_hdlr)
1586 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001587
Christian Heimes180510d2008-03-03 19:15:45 +00001588class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001589
Christian Heimes180510d2008-03-03 19:15:45 +00001590 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001591
Christian Heimes180510d2008-03-03 19:15:45 +00001592 def setUp(self):
1593 """Create a dict to remember potentially destroyed objects."""
1594 BaseTest.setUp(self)
1595 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001596
Christian Heimes180510d2008-03-03 19:15:45 +00001597 def _watch_for_survival(self, *args):
1598 """Watch the given objects for survival, by creating weakrefs to
1599 them."""
1600 for obj in args:
1601 key = id(obj), repr(obj)
1602 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001603
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001604 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001605 """Assert that all objects watched for survival have survived."""
1606 # Trigger cycle breaking.
1607 gc.collect()
1608 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001609 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001610 if ref() is None:
1611 dead.append(repr_)
1612 if dead:
1613 self.fail("%d objects should have survived "
1614 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001615
Christian Heimes180510d2008-03-03 19:15:45 +00001616 def test_persistent_loggers(self):
1617 # Logger objects are persistent and retain their configuration, even
1618 # if visible references are destroyed.
1619 self.root_logger.setLevel(logging.INFO)
1620 foo = logging.getLogger("foo")
1621 self._watch_for_survival(foo)
1622 foo.setLevel(logging.DEBUG)
1623 self.root_logger.debug(self.next_message())
1624 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001625 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001626 ('foo', 'DEBUG', '2'),
1627 ])
1628 del foo
1629 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001630 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001631 # foo has retained its settings.
1632 bar = logging.getLogger("foo")
1633 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001634 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001635 ('foo', 'DEBUG', '2'),
1636 ('foo', 'DEBUG', '3'),
1637 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001638
Benjamin Petersonf91df042009-02-13 02:50:59 +00001639
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001640class EncodingTest(BaseTest):
1641 def test_encoding_plain_file(self):
1642 # In Python 2.x, a plain file object is treated as having no encoding.
1643 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001644 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1645 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001646 # the non-ascii data we write to the log.
1647 data = "foo\x80"
1648 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001649 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001650 log.addHandler(handler)
1651 try:
1652 # write non-ascii data to the log.
1653 log.warning(data)
1654 finally:
1655 log.removeHandler(handler)
1656 handler.close()
1657 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001658 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001659 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001660 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001661 finally:
1662 f.close()
1663 finally:
1664 if os.path.isfile(fn):
1665 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001666
Benjamin Petersonf91df042009-02-13 02:50:59 +00001667 def test_encoding_cyrillic_unicode(self):
1668 log = logging.getLogger("test")
1669 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1670 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1671 #Ensure it's written in a Cyrillic encoding
1672 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001673 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001674 stream = io.BytesIO()
1675 writer = writer_class(stream, 'strict')
1676 handler = logging.StreamHandler(writer)
1677 log.addHandler(handler)
1678 try:
1679 log.warning(message)
1680 finally:
1681 log.removeHandler(handler)
1682 handler.close()
1683 # check we wrote exactly those bytes, ignoring trailing \n etc
1684 s = stream.getvalue()
1685 #Compare against what the data should be when encoded in CP-1251
1686 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1687
1688
Georg Brandlf9734072008-12-07 15:30:06 +00001689class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001690
Georg Brandlf9734072008-12-07 15:30:06 +00001691 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001692 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001693 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001694 self.addCleanup(lambda: logging.captureWarnings(False))
1695 warnings.filterwarnings("always", category=UserWarning)
1696 stream = io.StringIO()
1697 h = logging.StreamHandler(stream)
1698 logger = logging.getLogger("py.warnings")
1699 logger.addHandler(h)
1700 warnings.warn("I'm warning you...")
1701 logger.removeHandler(h)
1702 s = stream.getvalue()
1703 h.close()
1704 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001705
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001706 #See if an explicit file uses the original implementation
1707 a_file = io.StringIO()
1708 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1709 a_file, "Dummy line")
1710 s = a_file.getvalue()
1711 a_file.close()
1712 self.assertEqual(s,
1713 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1714
1715 def test_warnings_no_handlers(self):
1716 with warnings.catch_warnings():
1717 logging.captureWarnings(True)
1718 self.addCleanup(lambda: logging.captureWarnings(False))
1719
1720 # confirm our assumption: no loggers are set
1721 logger = logging.getLogger("py.warnings")
1722 assert logger.handlers == []
1723
1724 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1725 self.assertTrue(len(logger.handlers) == 1)
1726 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001727
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001728
1729def formatFunc(format, datefmt=None):
1730 return logging.Formatter(format, datefmt)
1731
1732def handlerFunc():
1733 return logging.StreamHandler()
1734
1735class CustomHandler(logging.StreamHandler):
1736 pass
1737
1738class ConfigDictTest(BaseTest):
1739
1740 """Reading logging config from a dictionary."""
1741
1742 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1743
1744 # config0 is a standard configuration.
1745 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001746 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001747 'formatters': {
1748 'form1' : {
1749 'format' : '%(levelname)s ++ %(message)s',
1750 },
1751 },
1752 'handlers' : {
1753 'hand1' : {
1754 'class' : 'logging.StreamHandler',
1755 'formatter' : 'form1',
1756 'level' : 'NOTSET',
1757 'stream' : 'ext://sys.stdout',
1758 },
1759 },
1760 'root' : {
1761 'level' : 'WARNING',
1762 'handlers' : ['hand1'],
1763 },
1764 }
1765
1766 # config1 adds a little to the standard configuration.
1767 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001768 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001769 'formatters': {
1770 'form1' : {
1771 'format' : '%(levelname)s ++ %(message)s',
1772 },
1773 },
1774 'handlers' : {
1775 'hand1' : {
1776 'class' : 'logging.StreamHandler',
1777 'formatter' : 'form1',
1778 'level' : 'NOTSET',
1779 'stream' : 'ext://sys.stdout',
1780 },
1781 },
1782 'loggers' : {
1783 'compiler.parser' : {
1784 'level' : 'DEBUG',
1785 'handlers' : ['hand1'],
1786 },
1787 },
1788 'root' : {
1789 'level' : 'WARNING',
1790 },
1791 }
1792
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001793 # config1a moves the handler to the root. Used with config8a
1794 config1a = {
1795 'version': 1,
1796 'formatters': {
1797 'form1' : {
1798 'format' : '%(levelname)s ++ %(message)s',
1799 },
1800 },
1801 'handlers' : {
1802 'hand1' : {
1803 'class' : 'logging.StreamHandler',
1804 'formatter' : 'form1',
1805 'level' : 'NOTSET',
1806 'stream' : 'ext://sys.stdout',
1807 },
1808 },
1809 'loggers' : {
1810 'compiler.parser' : {
1811 'level' : 'DEBUG',
1812 },
1813 },
1814 'root' : {
1815 'level' : 'WARNING',
1816 'handlers' : ['hand1'],
1817 },
1818 }
1819
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001820 # config2 has a subtle configuration error that should be reported
1821 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001822 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001823 'formatters': {
1824 'form1' : {
1825 'format' : '%(levelname)s ++ %(message)s',
1826 },
1827 },
1828 'handlers' : {
1829 'hand1' : {
1830 'class' : 'logging.StreamHandler',
1831 'formatter' : 'form1',
1832 'level' : 'NOTSET',
1833 'stream' : 'ext://sys.stdbout',
1834 },
1835 },
1836 'loggers' : {
1837 'compiler.parser' : {
1838 'level' : 'DEBUG',
1839 'handlers' : ['hand1'],
1840 },
1841 },
1842 'root' : {
1843 'level' : 'WARNING',
1844 },
1845 }
1846
1847 #As config1 but with a misspelt level on a handler
1848 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001849 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001850 'formatters': {
1851 'form1' : {
1852 'format' : '%(levelname)s ++ %(message)s',
1853 },
1854 },
1855 'handlers' : {
1856 'hand1' : {
1857 'class' : 'logging.StreamHandler',
1858 'formatter' : 'form1',
1859 'level' : 'NTOSET',
1860 'stream' : 'ext://sys.stdout',
1861 },
1862 },
1863 'loggers' : {
1864 'compiler.parser' : {
1865 'level' : 'DEBUG',
1866 'handlers' : ['hand1'],
1867 },
1868 },
1869 'root' : {
1870 'level' : 'WARNING',
1871 },
1872 }
1873
1874
1875 #As config1 but with a misspelt level on a logger
1876 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001877 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001878 'formatters': {
1879 'form1' : {
1880 'format' : '%(levelname)s ++ %(message)s',
1881 },
1882 },
1883 'handlers' : {
1884 'hand1' : {
1885 'class' : 'logging.StreamHandler',
1886 'formatter' : 'form1',
1887 'level' : 'NOTSET',
1888 'stream' : 'ext://sys.stdout',
1889 },
1890 },
1891 'loggers' : {
1892 'compiler.parser' : {
1893 'level' : 'DEBUG',
1894 'handlers' : ['hand1'],
1895 },
1896 },
1897 'root' : {
1898 'level' : 'WRANING',
1899 },
1900 }
1901
1902 # config3 has a less subtle configuration error
1903 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001904 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001905 'formatters': {
1906 'form1' : {
1907 'format' : '%(levelname)s ++ %(message)s',
1908 },
1909 },
1910 'handlers' : {
1911 'hand1' : {
1912 'class' : 'logging.StreamHandler',
1913 'formatter' : 'misspelled_name',
1914 'level' : 'NOTSET',
1915 'stream' : 'ext://sys.stdout',
1916 },
1917 },
1918 'loggers' : {
1919 'compiler.parser' : {
1920 'level' : 'DEBUG',
1921 'handlers' : ['hand1'],
1922 },
1923 },
1924 'root' : {
1925 'level' : 'WARNING',
1926 },
1927 }
1928
1929 # config4 specifies a custom formatter class to be loaded
1930 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001931 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001932 'formatters': {
1933 'form1' : {
1934 '()' : __name__ + '.ExceptionFormatter',
1935 'format' : '%(levelname)s:%(name)s:%(message)s',
1936 },
1937 },
1938 'handlers' : {
1939 'hand1' : {
1940 'class' : 'logging.StreamHandler',
1941 'formatter' : 'form1',
1942 'level' : 'NOTSET',
1943 'stream' : 'ext://sys.stdout',
1944 },
1945 },
1946 'root' : {
1947 'level' : 'NOTSET',
1948 'handlers' : ['hand1'],
1949 },
1950 }
1951
1952 # As config4 but using an actual callable rather than a string
1953 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001954 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001955 'formatters': {
1956 'form1' : {
1957 '()' : ExceptionFormatter,
1958 'format' : '%(levelname)s:%(name)s:%(message)s',
1959 },
1960 'form2' : {
1961 '()' : __name__ + '.formatFunc',
1962 'format' : '%(levelname)s:%(name)s:%(message)s',
1963 },
1964 'form3' : {
1965 '()' : formatFunc,
1966 'format' : '%(levelname)s:%(name)s:%(message)s',
1967 },
1968 },
1969 'handlers' : {
1970 'hand1' : {
1971 'class' : 'logging.StreamHandler',
1972 'formatter' : 'form1',
1973 'level' : 'NOTSET',
1974 'stream' : 'ext://sys.stdout',
1975 },
1976 'hand2' : {
1977 '()' : handlerFunc,
1978 },
1979 },
1980 'root' : {
1981 'level' : 'NOTSET',
1982 'handlers' : ['hand1'],
1983 },
1984 }
1985
1986 # config5 specifies a custom handler class to be loaded
1987 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001988 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001989 'formatters': {
1990 'form1' : {
1991 'format' : '%(levelname)s ++ %(message)s',
1992 },
1993 },
1994 'handlers' : {
1995 'hand1' : {
1996 'class' : __name__ + '.CustomHandler',
1997 'formatter' : 'form1',
1998 'level' : 'NOTSET',
1999 'stream' : 'ext://sys.stdout',
2000 },
2001 },
2002 'loggers' : {
2003 'compiler.parser' : {
2004 'level' : 'DEBUG',
2005 'handlers' : ['hand1'],
2006 },
2007 },
2008 'root' : {
2009 'level' : 'WARNING',
2010 },
2011 }
2012
2013 # config6 specifies a custom handler class to be loaded
2014 # but has bad arguments
2015 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002016 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002017 'formatters': {
2018 'form1' : {
2019 'format' : '%(levelname)s ++ %(message)s',
2020 },
2021 },
2022 'handlers' : {
2023 'hand1' : {
2024 'class' : __name__ + '.CustomHandler',
2025 'formatter' : 'form1',
2026 'level' : 'NOTSET',
2027 'stream' : 'ext://sys.stdout',
2028 '9' : 'invalid parameter name',
2029 },
2030 },
2031 'loggers' : {
2032 'compiler.parser' : {
2033 'level' : 'DEBUG',
2034 'handlers' : ['hand1'],
2035 },
2036 },
2037 'root' : {
2038 'level' : 'WARNING',
2039 },
2040 }
2041
2042 #config 7 does not define compiler.parser but defines compiler.lexer
2043 #so compiler.parser should be disabled after applying it
2044 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002045 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002046 'formatters': {
2047 'form1' : {
2048 'format' : '%(levelname)s ++ %(message)s',
2049 },
2050 },
2051 'handlers' : {
2052 'hand1' : {
2053 'class' : 'logging.StreamHandler',
2054 'formatter' : 'form1',
2055 'level' : 'NOTSET',
2056 'stream' : 'ext://sys.stdout',
2057 },
2058 },
2059 'loggers' : {
2060 'compiler.lexer' : {
2061 'level' : 'DEBUG',
2062 'handlers' : ['hand1'],
2063 },
2064 },
2065 'root' : {
2066 'level' : 'WARNING',
2067 },
2068 }
2069
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002070 # config8 defines both compiler and compiler.lexer
2071 # so compiler.parser should not be disabled (since
2072 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002073 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002074 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002075 'disable_existing_loggers' : False,
2076 'formatters': {
2077 'form1' : {
2078 'format' : '%(levelname)s ++ %(message)s',
2079 },
2080 },
2081 'handlers' : {
2082 'hand1' : {
2083 'class' : 'logging.StreamHandler',
2084 'formatter' : 'form1',
2085 'level' : 'NOTSET',
2086 'stream' : 'ext://sys.stdout',
2087 },
2088 },
2089 'loggers' : {
2090 'compiler' : {
2091 'level' : 'DEBUG',
2092 'handlers' : ['hand1'],
2093 },
2094 'compiler.lexer' : {
2095 },
2096 },
2097 'root' : {
2098 'level' : 'WARNING',
2099 },
2100 }
2101
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002102 # config8a disables existing loggers
2103 config8a = {
2104 'version': 1,
2105 'disable_existing_loggers' : True,
2106 'formatters': {
2107 'form1' : {
2108 'format' : '%(levelname)s ++ %(message)s',
2109 },
2110 },
2111 'handlers' : {
2112 'hand1' : {
2113 'class' : 'logging.StreamHandler',
2114 'formatter' : 'form1',
2115 'level' : 'NOTSET',
2116 'stream' : 'ext://sys.stdout',
2117 },
2118 },
2119 'loggers' : {
2120 'compiler' : {
2121 'level' : 'DEBUG',
2122 'handlers' : ['hand1'],
2123 },
2124 'compiler.lexer' : {
2125 },
2126 },
2127 'root' : {
2128 'level' : 'WARNING',
2129 },
2130 }
2131
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002132 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002133 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002134 'formatters': {
2135 'form1' : {
2136 'format' : '%(levelname)s ++ %(message)s',
2137 },
2138 },
2139 'handlers' : {
2140 'hand1' : {
2141 'class' : 'logging.StreamHandler',
2142 'formatter' : 'form1',
2143 'level' : 'WARNING',
2144 'stream' : 'ext://sys.stdout',
2145 },
2146 },
2147 'loggers' : {
2148 'compiler.parser' : {
2149 'level' : 'WARNING',
2150 'handlers' : ['hand1'],
2151 },
2152 },
2153 'root' : {
2154 'level' : 'NOTSET',
2155 },
2156 }
2157
2158 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002159 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002160 'incremental' : True,
2161 'handlers' : {
2162 'hand1' : {
2163 'level' : 'WARNING',
2164 },
2165 },
2166 'loggers' : {
2167 'compiler.parser' : {
2168 'level' : 'INFO',
2169 },
2170 },
2171 }
2172
2173 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002174 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002175 'incremental' : True,
2176 'handlers' : {
2177 'hand1' : {
2178 'level' : 'INFO',
2179 },
2180 },
2181 'loggers' : {
2182 'compiler.parser' : {
2183 'level' : 'INFO',
2184 },
2185 },
2186 }
2187
2188 #As config1 but with a filter added
2189 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002190 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002191 'formatters': {
2192 'form1' : {
2193 'format' : '%(levelname)s ++ %(message)s',
2194 },
2195 },
2196 'filters' : {
2197 'filt1' : {
2198 'name' : 'compiler.parser',
2199 },
2200 },
2201 'handlers' : {
2202 'hand1' : {
2203 'class' : 'logging.StreamHandler',
2204 'formatter' : 'form1',
2205 'level' : 'NOTSET',
2206 'stream' : 'ext://sys.stdout',
2207 'filters' : ['filt1'],
2208 },
2209 },
2210 'loggers' : {
2211 'compiler.parser' : {
2212 'level' : 'DEBUG',
2213 'filters' : ['filt1'],
2214 },
2215 },
2216 'root' : {
2217 'level' : 'WARNING',
2218 'handlers' : ['hand1'],
2219 },
2220 }
2221
2222 #As config1 but using cfg:// references
2223 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002224 'version': 1,
2225 'true_formatters': {
2226 'form1' : {
2227 'format' : '%(levelname)s ++ %(message)s',
2228 },
2229 },
2230 'handler_configs': {
2231 'hand1' : {
2232 'class' : 'logging.StreamHandler',
2233 'formatter' : 'form1',
2234 'level' : 'NOTSET',
2235 'stream' : 'ext://sys.stdout',
2236 },
2237 },
2238 'formatters' : 'cfg://true_formatters',
2239 'handlers' : {
2240 'hand1' : 'cfg://handler_configs[hand1]',
2241 },
2242 'loggers' : {
2243 'compiler.parser' : {
2244 'level' : 'DEBUG',
2245 'handlers' : ['hand1'],
2246 },
2247 },
2248 'root' : {
2249 'level' : 'WARNING',
2250 },
2251 }
2252
2253 #As config11 but missing the version key
2254 config12 = {
2255 'true_formatters': {
2256 'form1' : {
2257 'format' : '%(levelname)s ++ %(message)s',
2258 },
2259 },
2260 'handler_configs': {
2261 'hand1' : {
2262 'class' : 'logging.StreamHandler',
2263 'formatter' : 'form1',
2264 'level' : 'NOTSET',
2265 'stream' : 'ext://sys.stdout',
2266 },
2267 },
2268 'formatters' : 'cfg://true_formatters',
2269 'handlers' : {
2270 'hand1' : 'cfg://handler_configs[hand1]',
2271 },
2272 'loggers' : {
2273 'compiler.parser' : {
2274 'level' : 'DEBUG',
2275 'handlers' : ['hand1'],
2276 },
2277 },
2278 'root' : {
2279 'level' : 'WARNING',
2280 },
2281 }
2282
2283 #As config11 but using an unsupported version
2284 config13 = {
2285 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002286 'true_formatters': {
2287 'form1' : {
2288 'format' : '%(levelname)s ++ %(message)s',
2289 },
2290 },
2291 'handler_configs': {
2292 'hand1' : {
2293 'class' : 'logging.StreamHandler',
2294 'formatter' : 'form1',
2295 'level' : 'NOTSET',
2296 'stream' : 'ext://sys.stdout',
2297 },
2298 },
2299 'formatters' : 'cfg://true_formatters',
2300 'handlers' : {
2301 'hand1' : 'cfg://handler_configs[hand1]',
2302 },
2303 'loggers' : {
2304 'compiler.parser' : {
2305 'level' : 'DEBUG',
2306 'handlers' : ['hand1'],
2307 },
2308 },
2309 'root' : {
2310 'level' : 'WARNING',
2311 },
2312 }
2313
2314 def apply_config(self, conf):
2315 logging.config.dictConfig(conf)
2316
2317 def test_config0_ok(self):
2318 # A simple config which overrides the default settings.
2319 with captured_stdout() as output:
2320 self.apply_config(self.config0)
2321 logger = logging.getLogger()
2322 # Won't output anything
2323 logger.info(self.next_message())
2324 # Outputs a message
2325 logger.error(self.next_message())
2326 self.assert_log_lines([
2327 ('ERROR', '2'),
2328 ], stream=output)
2329 # Original logger output is empty.
2330 self.assert_log_lines([])
2331
2332 def test_config1_ok(self, config=config1):
2333 # A config defining a sub-parser as well.
2334 with captured_stdout() as output:
2335 self.apply_config(config)
2336 logger = logging.getLogger("compiler.parser")
2337 # Both will output a message
2338 logger.info(self.next_message())
2339 logger.error(self.next_message())
2340 self.assert_log_lines([
2341 ('INFO', '1'),
2342 ('ERROR', '2'),
2343 ], stream=output)
2344 # Original logger output is empty.
2345 self.assert_log_lines([])
2346
2347 def test_config2_failure(self):
2348 # A simple config which overrides the default settings.
2349 self.assertRaises(Exception, self.apply_config, self.config2)
2350
2351 def test_config2a_failure(self):
2352 # A simple config which overrides the default settings.
2353 self.assertRaises(Exception, self.apply_config, self.config2a)
2354
2355 def test_config2b_failure(self):
2356 # A simple config which overrides the default settings.
2357 self.assertRaises(Exception, self.apply_config, self.config2b)
2358
2359 def test_config3_failure(self):
2360 # A simple config which overrides the default settings.
2361 self.assertRaises(Exception, self.apply_config, self.config3)
2362
2363 def test_config4_ok(self):
2364 # A config specifying a custom formatter class.
2365 with captured_stdout() as output:
2366 self.apply_config(self.config4)
2367 #logger = logging.getLogger()
2368 try:
2369 raise RuntimeError()
2370 except RuntimeError:
2371 logging.exception("just testing")
2372 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002373 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002374 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2375 # Original logger output is empty
2376 self.assert_log_lines([])
2377
2378 def test_config4a_ok(self):
2379 # A config specifying a custom formatter class.
2380 with captured_stdout() as output:
2381 self.apply_config(self.config4a)
2382 #logger = logging.getLogger()
2383 try:
2384 raise RuntimeError()
2385 except RuntimeError:
2386 logging.exception("just testing")
2387 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002388 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002389 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2390 # Original logger output is empty
2391 self.assert_log_lines([])
2392
2393 def test_config5_ok(self):
2394 self.test_config1_ok(config=self.config5)
2395
2396 def test_config6_failure(self):
2397 self.assertRaises(Exception, self.apply_config, self.config6)
2398
2399 def test_config7_ok(self):
2400 with captured_stdout() as output:
2401 self.apply_config(self.config1)
2402 logger = logging.getLogger("compiler.parser")
2403 # Both will output a message
2404 logger.info(self.next_message())
2405 logger.error(self.next_message())
2406 self.assert_log_lines([
2407 ('INFO', '1'),
2408 ('ERROR', '2'),
2409 ], stream=output)
2410 # Original logger output is empty.
2411 self.assert_log_lines([])
2412 with captured_stdout() as output:
2413 self.apply_config(self.config7)
2414 logger = logging.getLogger("compiler.parser")
2415 self.assertTrue(logger.disabled)
2416 logger = logging.getLogger("compiler.lexer")
2417 # Both will output a message
2418 logger.info(self.next_message())
2419 logger.error(self.next_message())
2420 self.assert_log_lines([
2421 ('INFO', '3'),
2422 ('ERROR', '4'),
2423 ], stream=output)
2424 # Original logger output is empty.
2425 self.assert_log_lines([])
2426
2427 #Same as test_config_7_ok but don't disable old loggers.
2428 def test_config_8_ok(self):
2429 with captured_stdout() as output:
2430 self.apply_config(self.config1)
2431 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002432 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002433 logger.info(self.next_message())
2434 logger.error(self.next_message())
2435 self.assert_log_lines([
2436 ('INFO', '1'),
2437 ('ERROR', '2'),
2438 ], stream=output)
2439 # Original logger output is empty.
2440 self.assert_log_lines([])
2441 with captured_stdout() as output:
2442 self.apply_config(self.config8)
2443 logger = logging.getLogger("compiler.parser")
2444 self.assertFalse(logger.disabled)
2445 # Both will output a message
2446 logger.info(self.next_message())
2447 logger.error(self.next_message())
2448 logger = logging.getLogger("compiler.lexer")
2449 # Both will output a message
2450 logger.info(self.next_message())
2451 logger.error(self.next_message())
2452 self.assert_log_lines([
2453 ('INFO', '3'),
2454 ('ERROR', '4'),
2455 ('INFO', '5'),
2456 ('ERROR', '6'),
2457 ], stream=output)
2458 # Original logger output is empty.
2459 self.assert_log_lines([])
2460
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002461 def test_config_8a_ok(self):
2462 with captured_stdout() as output:
2463 self.apply_config(self.config1a)
2464 logger = logging.getLogger("compiler.parser")
2465 # See issue #11424. compiler-hyphenated sorts
2466 # between compiler and compiler.xyz and this
2467 # was preventing compiler.xyz from being included
2468 # in the child loggers of compiler because of an
2469 # overzealous loop termination condition.
2470 hyphenated = logging.getLogger('compiler-hyphenated')
2471 # All will output a message
2472 logger.info(self.next_message())
2473 logger.error(self.next_message())
2474 hyphenated.critical(self.next_message())
2475 self.assert_log_lines([
2476 ('INFO', '1'),
2477 ('ERROR', '2'),
2478 ('CRITICAL', '3'),
2479 ], stream=output)
2480 # Original logger output is empty.
2481 self.assert_log_lines([])
2482 with captured_stdout() as output:
2483 self.apply_config(self.config8a)
2484 logger = logging.getLogger("compiler.parser")
2485 self.assertFalse(logger.disabled)
2486 # Both will output a message
2487 logger.info(self.next_message())
2488 logger.error(self.next_message())
2489 logger = logging.getLogger("compiler.lexer")
2490 # Both will output a message
2491 logger.info(self.next_message())
2492 logger.error(self.next_message())
2493 # Will not appear
2494 hyphenated.critical(self.next_message())
2495 self.assert_log_lines([
2496 ('INFO', '4'),
2497 ('ERROR', '5'),
2498 ('INFO', '6'),
2499 ('ERROR', '7'),
2500 ], stream=output)
2501 # Original logger output is empty.
2502 self.assert_log_lines([])
2503
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002504 def test_config_9_ok(self):
2505 with captured_stdout() as output:
2506 self.apply_config(self.config9)
2507 logger = logging.getLogger("compiler.parser")
2508 #Nothing will be output since both handler and logger are set to WARNING
2509 logger.info(self.next_message())
2510 self.assert_log_lines([], stream=output)
2511 self.apply_config(self.config9a)
2512 #Nothing will be output since both handler is still set to WARNING
2513 logger.info(self.next_message())
2514 self.assert_log_lines([], stream=output)
2515 self.apply_config(self.config9b)
2516 #Message should now be output
2517 logger.info(self.next_message())
2518 self.assert_log_lines([
2519 ('INFO', '3'),
2520 ], stream=output)
2521
2522 def test_config_10_ok(self):
2523 with captured_stdout() as output:
2524 self.apply_config(self.config10)
2525 logger = logging.getLogger("compiler.parser")
2526 logger.warning(self.next_message())
2527 logger = logging.getLogger('compiler')
2528 #Not output, because filtered
2529 logger.warning(self.next_message())
2530 logger = logging.getLogger('compiler.lexer')
2531 #Not output, because filtered
2532 logger.warning(self.next_message())
2533 logger = logging.getLogger("compiler.parser.codegen")
2534 #Output, as not filtered
2535 logger.error(self.next_message())
2536 self.assert_log_lines([
2537 ('WARNING', '1'),
2538 ('ERROR', '4'),
2539 ], stream=output)
2540
2541 def test_config11_ok(self):
2542 self.test_config1_ok(self.config11)
2543
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002544 def test_config12_failure(self):
2545 self.assertRaises(Exception, self.apply_config, self.config12)
2546
2547 def test_config13_failure(self):
2548 self.assertRaises(Exception, self.apply_config, self.config13)
2549
Victor Stinner45df8202010-04-28 22:31:17 +00002550 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002551 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002552 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002553 # Ask for a randomly assigned port (by using port 0)
2554 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002555 t.start()
2556 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002557 # Now get the port allocated
2558 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002559 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002560 try:
2561 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2562 sock.settimeout(2.0)
2563 sock.connect(('localhost', port))
2564
2565 slen = struct.pack('>L', len(text))
2566 s = slen + text
2567 sentsofar = 0
2568 left = len(s)
2569 while left > 0:
2570 sent = sock.send(s[sentsofar:])
2571 sentsofar += sent
2572 left -= sent
2573 sock.close()
2574 finally:
2575 t.ready.wait(2.0)
2576 logging.config.stopListening()
2577 t.join(2.0)
2578
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002579 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002580 def test_listen_config_10_ok(self):
2581 with captured_stdout() as output:
2582 self.setup_via_listener(json.dumps(self.config10))
2583 logger = logging.getLogger("compiler.parser")
2584 logger.warning(self.next_message())
2585 logger = logging.getLogger('compiler')
2586 #Not output, because filtered
2587 logger.warning(self.next_message())
2588 logger = logging.getLogger('compiler.lexer')
2589 #Not output, because filtered
2590 logger.warning(self.next_message())
2591 logger = logging.getLogger("compiler.parser.codegen")
2592 #Output, as not filtered
2593 logger.error(self.next_message())
2594 self.assert_log_lines([
2595 ('WARNING', '1'),
2596 ('ERROR', '4'),
2597 ], stream=output)
2598
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002599 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002600 def test_listen_config_1_ok(self):
2601 with captured_stdout() as output:
2602 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2603 logger = logging.getLogger("compiler.parser")
2604 # Both will output a message
2605 logger.info(self.next_message())
2606 logger.error(self.next_message())
2607 self.assert_log_lines([
2608 ('INFO', '1'),
2609 ('ERROR', '2'),
2610 ], stream=output)
2611 # Original logger output is empty.
2612 self.assert_log_lines([])
2613
Vinay Sajip373baef2011-04-26 20:05:24 +01002614 def test_baseconfig(self):
2615 d = {
2616 'atuple': (1, 2, 3),
2617 'alist': ['a', 'b', 'c'],
2618 'adict': {'d': 'e', 'f': 3 },
2619 'nest1': ('g', ('h', 'i'), 'j'),
2620 'nest2': ['k', ['l', 'm'], 'n'],
2621 'nest3': ['o', 'cfg://alist', 'p'],
2622 }
2623 bc = logging.config.BaseConfigurator(d)
2624 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2625 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2626 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2627 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2628 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2629 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2630 v = bc.convert('cfg://nest3')
2631 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2632 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2633 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2634 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002635
2636class ManagerTest(BaseTest):
2637 def test_manager_loggerclass(self):
2638 logged = []
2639
2640 class MyLogger(logging.Logger):
2641 def _log(self, level, msg, args, exc_info=None, extra=None):
2642 logged.append(msg)
2643
2644 man = logging.Manager(None)
2645 self.assertRaises(TypeError, man.setLoggerClass, int)
2646 man.setLoggerClass(MyLogger)
2647 logger = man.getLogger('test')
2648 logger.warning('should appear in logged')
2649 logging.warning('should not appear in logged')
2650
2651 self.assertEqual(logged, ['should appear in logged'])
2652
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002653 def test_set_log_record_factory(self):
2654 man = logging.Manager(None)
2655 expected = object()
2656 man.setLogRecordFactory(expected)
2657 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002658
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002659class ChildLoggerTest(BaseTest):
2660 def test_child_loggers(self):
2661 r = logging.getLogger()
2662 l1 = logging.getLogger('abc')
2663 l2 = logging.getLogger('def.ghi')
2664 c1 = r.getChild('xyz')
2665 c2 = r.getChild('uvw.xyz')
2666 self.assertTrue(c1 is logging.getLogger('xyz'))
2667 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2668 c1 = l1.getChild('def')
2669 c2 = c1.getChild('ghi')
2670 c3 = l1.getChild('def.ghi')
2671 self.assertTrue(c1 is logging.getLogger('abc.def'))
2672 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2673 self.assertTrue(c2 is c3)
2674
2675
Vinay Sajip6fac8172010-10-19 20:44:14 +00002676class DerivedLogRecord(logging.LogRecord):
2677 pass
2678
Vinay Sajip61561522010-12-03 11:50:38 +00002679class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002680
2681 def setUp(self):
2682 class CheckingFilter(logging.Filter):
2683 def __init__(self, cls):
2684 self.cls = cls
2685
2686 def filter(self, record):
2687 t = type(record)
2688 if t is not self.cls:
2689 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2690 self.cls)
2691 raise TypeError(msg)
2692 return True
2693
2694 BaseTest.setUp(self)
2695 self.filter = CheckingFilter(DerivedLogRecord)
2696 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002697 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002698
2699 def tearDown(self):
2700 self.root_logger.removeFilter(self.filter)
2701 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002702 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002703
2704 def test_logrecord_class(self):
2705 self.assertRaises(TypeError, self.root_logger.warning,
2706 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002707 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002708 self.root_logger.error(self.next_message())
2709 self.assert_log_lines([
2710 ('root', 'ERROR', '2'),
2711 ])
2712
2713
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002714class QueueHandlerTest(BaseTest):
2715 # Do not bother with a logger name group.
2716 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2717
2718 def setUp(self):
2719 BaseTest.setUp(self)
2720 self.queue = queue.Queue(-1)
2721 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2722 self.que_logger = logging.getLogger('que')
2723 self.que_logger.propagate = False
2724 self.que_logger.setLevel(logging.WARNING)
2725 self.que_logger.addHandler(self.que_hdlr)
2726
2727 def tearDown(self):
2728 self.que_hdlr.close()
2729 BaseTest.tearDown(self)
2730
2731 def test_queue_handler(self):
2732 self.que_logger.debug(self.next_message())
2733 self.assertRaises(queue.Empty, self.queue.get_nowait)
2734 self.que_logger.info(self.next_message())
2735 self.assertRaises(queue.Empty, self.queue.get_nowait)
2736 msg = self.next_message()
2737 self.que_logger.warning(msg)
2738 data = self.queue.get_nowait()
2739 self.assertTrue(isinstance(data, logging.LogRecord))
2740 self.assertEqual(data.name, self.que_logger.name)
2741 self.assertEqual((data.msg, data.args), (msg, None))
2742
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002743 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2744 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002745 def test_queue_listener(self):
2746 handler = TestHandler(Matcher())
2747 listener = logging.handlers.QueueListener(self.queue, handler)
2748 listener.start()
2749 try:
2750 self.que_logger.warning(self.next_message())
2751 self.que_logger.error(self.next_message())
2752 self.que_logger.critical(self.next_message())
2753 finally:
2754 listener.stop()
2755 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2756 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2757 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2758
Vinay Sajip37eb3382011-04-26 20:26:41 +01002759ZERO = datetime.timedelta(0)
2760
2761class UTC(datetime.tzinfo):
2762 def utcoffset(self, dt):
2763 return ZERO
2764
2765 dst = utcoffset
2766
2767 def tzname(self, dt):
2768 return 'UTC'
2769
2770utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002771
Vinay Sajipa39c5712010-10-25 13:57:39 +00002772class FormatterTest(unittest.TestCase):
2773 def setUp(self):
2774 self.common = {
2775 'name': 'formatter.test',
2776 'level': logging.DEBUG,
2777 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2778 'lineno': 42,
2779 'exc_info': None,
2780 'func': None,
2781 'msg': 'Message with %d %s',
2782 'args': (2, 'placeholders'),
2783 }
2784 self.variants = {
2785 }
2786
2787 def get_record(self, name=None):
2788 result = dict(self.common)
2789 if name is not None:
2790 result.update(self.variants[name])
2791 return logging.makeLogRecord(result)
2792
2793 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002794 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002795 r = self.get_record()
2796 f = logging.Formatter('${%(message)s}')
2797 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2798 f = logging.Formatter('%(random)s')
2799 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002800 self.assertFalse(f.usesTime())
2801 f = logging.Formatter('%(asctime)s')
2802 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002803 f = logging.Formatter('%(asctime)-15s')
2804 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002805 f = logging.Formatter('asctime')
2806 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002807
2808 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002809 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002810 r = self.get_record()
2811 f = logging.Formatter('$%{message}%$', style='{')
2812 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2813 f = logging.Formatter('{random}', style='{')
2814 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002815 self.assertFalse(f.usesTime())
2816 f = logging.Formatter('{asctime}', style='{')
2817 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002818 f = logging.Formatter('{asctime!s:15}', style='{')
2819 self.assertTrue(f.usesTime())
2820 f = logging.Formatter('{asctime:15}', style='{')
2821 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002822 f = logging.Formatter('asctime', style='{')
2823 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002824
2825 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002826 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002827 r = self.get_record()
2828 f = logging.Formatter('$message', style='$')
2829 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2830 f = logging.Formatter('$$%${message}%$$', style='$')
2831 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2832 f = logging.Formatter('${random}', style='$')
2833 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002834 self.assertFalse(f.usesTime())
2835 f = logging.Formatter('${asctime}', style='$')
2836 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002837 f = logging.Formatter('${asctime', style='$')
2838 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002839 f = logging.Formatter('$asctime', style='$')
2840 self.assertTrue(f.usesTime())
2841 f = logging.Formatter('asctime', style='$')
2842 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002843
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002844 def test_invalid_style(self):
2845 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2846
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002847 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002848 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002849 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002850 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002851 r.msecs = 123
2852 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002853 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002854 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2855 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002856 f.format(r)
2857 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2858
2859class TestBufferingFormatter(logging.BufferingFormatter):
2860 def formatHeader(self, records):
2861 return '[(%d)' % len(records)
2862
2863 def formatFooter(self, records):
2864 return '(%d)]' % len(records)
2865
2866class BufferingFormatterTest(unittest.TestCase):
2867 def setUp(self):
2868 self.records = [
2869 logging.makeLogRecord({'msg': 'one'}),
2870 logging.makeLogRecord({'msg': 'two'}),
2871 ]
2872
2873 def test_default(self):
2874 f = logging.BufferingFormatter()
2875 self.assertEqual('', f.format([]))
2876 self.assertEqual('onetwo', f.format(self.records))
2877
2878 def test_custom(self):
2879 f = TestBufferingFormatter()
2880 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2881 lf = logging.Formatter('<%(message)s>')
2882 f = TestBufferingFormatter(lf)
2883 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002884
2885class ExceptionTest(BaseTest):
2886 def test_formatting(self):
2887 r = self.root_logger
2888 h = RecordingHandler()
2889 r.addHandler(h)
2890 try:
2891 raise RuntimeError('deliberate mistake')
2892 except:
2893 logging.exception('failed', stack_info=True)
2894 r.removeHandler(h)
2895 h.close()
2896 r = h.records[0]
2897 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2898 'call last):\n'))
2899 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2900 'deliberate mistake'))
2901 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2902 'call last):\n'))
2903 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2904 'stack_info=True)'))
2905
2906
Vinay Sajip5a27d402010-12-10 11:42:57 +00002907class LastResortTest(BaseTest):
2908 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002909 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002910 root = self.root_logger
2911 root.removeHandler(self.root_hdlr)
2912 old_stderr = sys.stderr
2913 old_lastresort = logging.lastResort
2914 old_raise_exceptions = logging.raiseExceptions
2915 try:
2916 sys.stderr = sio = io.StringIO()
2917 root.warning('This is your final chance!')
2918 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2919 #No handlers and no last resort, so 'No handlers' message
2920 logging.lastResort = None
2921 sys.stderr = sio = io.StringIO()
2922 root.warning('This is your final chance!')
2923 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2924 # 'No handlers' message only printed once
2925 sys.stderr = sio = io.StringIO()
2926 root.warning('This is your final chance!')
2927 self.assertEqual(sio.getvalue(), '')
2928 root.manager.emittedNoHandlerWarning = False
2929 #If raiseExceptions is False, no message is printed
2930 logging.raiseExceptions = False
2931 sys.stderr = sio = io.StringIO()
2932 root.warning('This is your final chance!')
2933 self.assertEqual(sio.getvalue(), '')
2934 finally:
2935 sys.stderr = old_stderr
2936 root.addHandler(self.root_hdlr)
2937 logging.lastResort = old_lastresort
2938 logging.raiseExceptions = old_raise_exceptions
2939
2940
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002941class FakeHandler:
2942
2943 def __init__(self, identifier, called):
2944 for method in ('acquire', 'flush', 'close', 'release'):
2945 setattr(self, method, self.record_call(identifier, method, called))
2946
2947 def record_call(self, identifier, method_name, called):
2948 def inner():
2949 called.append('{} - {}'.format(identifier, method_name))
2950 return inner
2951
2952
2953class RecordingHandler(logging.NullHandler):
2954
2955 def __init__(self, *args, **kwargs):
2956 super(RecordingHandler, self).__init__(*args, **kwargs)
2957 self.records = []
2958
2959 def handle(self, record):
2960 """Keep track of all the emitted records."""
2961 self.records.append(record)
2962
2963
2964class ShutdownTest(BaseTest):
2965
Vinay Sajip5e66b162011-04-20 15:41:14 +01002966 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002967
2968 def setUp(self):
2969 super(ShutdownTest, self).setUp()
2970 self.called = []
2971
2972 raise_exceptions = logging.raiseExceptions
2973 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2974
2975 def raise_error(self, error):
2976 def inner():
2977 raise error()
2978 return inner
2979
2980 def test_no_failure(self):
2981 # create some fake handlers
2982 handler0 = FakeHandler(0, self.called)
2983 handler1 = FakeHandler(1, self.called)
2984 handler2 = FakeHandler(2, self.called)
2985
2986 # create live weakref to those handlers
2987 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2988
2989 logging.shutdown(handlerList=list(handlers))
2990
2991 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2992 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2993 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2994 self.assertEqual(expected, self.called)
2995
2996 def _test_with_failure_in_method(self, method, error):
2997 handler = FakeHandler(0, self.called)
2998 setattr(handler, method, self.raise_error(error))
2999 handlers = [logging.weakref.ref(handler)]
3000
3001 logging.shutdown(handlerList=list(handlers))
3002
3003 self.assertEqual('0 - release', self.called[-1])
3004
3005 def test_with_ioerror_in_acquire(self):
3006 self._test_with_failure_in_method('acquire', IOError)
3007
3008 def test_with_ioerror_in_flush(self):
3009 self._test_with_failure_in_method('flush', IOError)
3010
3011 def test_with_ioerror_in_close(self):
3012 self._test_with_failure_in_method('close', IOError)
3013
3014 def test_with_valueerror_in_acquire(self):
3015 self._test_with_failure_in_method('acquire', ValueError)
3016
3017 def test_with_valueerror_in_flush(self):
3018 self._test_with_failure_in_method('flush', ValueError)
3019
3020 def test_with_valueerror_in_close(self):
3021 self._test_with_failure_in_method('close', ValueError)
3022
3023 def test_with_other_error_in_acquire_without_raise(self):
3024 logging.raiseExceptions = False
3025 self._test_with_failure_in_method('acquire', IndexError)
3026
3027 def test_with_other_error_in_flush_without_raise(self):
3028 logging.raiseExceptions = False
3029 self._test_with_failure_in_method('flush', IndexError)
3030
3031 def test_with_other_error_in_close_without_raise(self):
3032 logging.raiseExceptions = False
3033 self._test_with_failure_in_method('close', IndexError)
3034
3035 def test_with_other_error_in_acquire_with_raise(self):
3036 logging.raiseExceptions = True
3037 self.assertRaises(IndexError, self._test_with_failure_in_method,
3038 'acquire', IndexError)
3039
3040 def test_with_other_error_in_flush_with_raise(self):
3041 logging.raiseExceptions = True
3042 self.assertRaises(IndexError, self._test_with_failure_in_method,
3043 'flush', IndexError)
3044
3045 def test_with_other_error_in_close_with_raise(self):
3046 logging.raiseExceptions = True
3047 self.assertRaises(IndexError, self._test_with_failure_in_method,
3048 'close', IndexError)
3049
3050
3051class ModuleLevelMiscTest(BaseTest):
3052
Vinay Sajip5e66b162011-04-20 15:41:14 +01003053 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003054
3055 def test_disable(self):
3056 old_disable = logging.root.manager.disable
3057 # confirm our assumptions are correct
3058 assert old_disable == 0
3059 self.addCleanup(lambda: logging.disable(old_disable))
3060
3061 logging.disable(83)
3062 self.assertEqual(logging.root.manager.disable, 83)
3063
3064 def _test_log(self, method, level=None):
3065 called = []
3066 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003067 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003068
3069 recording = RecordingHandler()
3070 logging.root.addHandler(recording)
3071
3072 log_method = getattr(logging, method)
3073 if level is not None:
3074 log_method(level, "test me: %r", recording)
3075 else:
3076 log_method("test me: %r", recording)
3077
3078 self.assertEqual(len(recording.records), 1)
3079 record = recording.records[0]
3080 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3081
3082 expected_level = level if level is not None else getattr(logging, method.upper())
3083 self.assertEqual(record.levelno, expected_level)
3084
3085 # basicConfig was not called!
3086 self.assertEqual(called, [])
3087
3088 def test_log(self):
3089 self._test_log('log', logging.ERROR)
3090
3091 def test_debug(self):
3092 self._test_log('debug')
3093
3094 def test_info(self):
3095 self._test_log('info')
3096
3097 def test_warning(self):
3098 self._test_log('warning')
3099
3100 def test_error(self):
3101 self._test_log('error')
3102
3103 def test_critical(self):
3104 self._test_log('critical')
3105
3106 def test_set_logger_class(self):
3107 self.assertRaises(TypeError, logging.setLoggerClass, object)
3108
3109 class MyLogger(logging.Logger):
3110 pass
3111
3112 logging.setLoggerClass(MyLogger)
3113 self.assertEqual(logging.getLoggerClass(), MyLogger)
3114
3115 logging.setLoggerClass(logging.Logger)
3116 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3117
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003118class LogRecordTest(BaseTest):
3119 def test_str_rep(self):
3120 r = logging.makeLogRecord({})
3121 s = str(r)
3122 self.assertTrue(s.startswith('<LogRecord: '))
3123 self.assertTrue(s.endswith('>'))
3124
3125 def test_dict_arg(self):
3126 h = RecordingHandler()
3127 r = logging.getLogger()
3128 r.addHandler(h)
3129 d = {'less' : 'more' }
3130 logging.warning('less is %(less)s', d)
3131 self.assertIs(h.records[0].args, d)
3132 self.assertEqual(h.records[0].message, 'less is more')
3133 r.removeHandler(h)
3134 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003135
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003136 def test_multiprocessing(self):
3137 r = logging.makeLogRecord({})
3138 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003139 try:
3140 import multiprocessing as mp
3141 r = logging.makeLogRecord({})
3142 self.assertEqual(r.processName, mp.current_process().name)
3143 except ImportError:
3144 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003145
3146 def test_optional(self):
3147 r = logging.makeLogRecord({})
3148 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003149 if threading:
3150 NOT_NONE(r.thread)
3151 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003152 NOT_NONE(r.process)
3153 NOT_NONE(r.processName)
3154 log_threads = logging.logThreads
3155 log_processes = logging.logProcesses
3156 log_multiprocessing = logging.logMultiprocessing
3157 try:
3158 logging.logThreads = False
3159 logging.logProcesses = False
3160 logging.logMultiprocessing = False
3161 r = logging.makeLogRecord({})
3162 NONE = self.assertIsNone
3163 NONE(r.thread)
3164 NONE(r.threadName)
3165 NONE(r.process)
3166 NONE(r.processName)
3167 finally:
3168 logging.logThreads = log_threads
3169 logging.logProcesses = log_processes
3170 logging.logMultiprocessing = log_multiprocessing
3171
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003172class BasicConfigTest(unittest.TestCase):
3173
Vinay Sajip95bf5042011-04-20 11:50:56 +01003174 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003175
3176 def setUp(self):
3177 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003178 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003179 self.saved_handlers = logging._handlers.copy()
3180 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003181 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003182 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003183 logging.root.handlers = []
3184
3185 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003186 for h in logging.root.handlers[:]:
3187 logging.root.removeHandler(h)
3188 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003189 super(BasicConfigTest, self).tearDown()
3190
Vinay Sajip3def7e02011-04-20 10:58:06 +01003191 def cleanup(self):
3192 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003193 logging._handlers.clear()
3194 logging._handlers.update(self.saved_handlers)
3195 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003196 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003197
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003198 def test_no_kwargs(self):
3199 logging.basicConfig()
3200
3201 # handler defaults to a StreamHandler to sys.stderr
3202 self.assertEqual(len(logging.root.handlers), 1)
3203 handler = logging.root.handlers[0]
3204 self.assertIsInstance(handler, logging.StreamHandler)
3205 self.assertEqual(handler.stream, sys.stderr)
3206
3207 formatter = handler.formatter
3208 # format defaults to logging.BASIC_FORMAT
3209 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3210 # datefmt defaults to None
3211 self.assertIsNone(formatter.datefmt)
3212 # style defaults to %
3213 self.assertIsInstance(formatter._style, logging.PercentStyle)
3214
3215 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003216 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003217
3218 def test_filename(self):
3219 logging.basicConfig(filename='test.log')
3220
3221 self.assertEqual(len(logging.root.handlers), 1)
3222 handler = logging.root.handlers[0]
3223 self.assertIsInstance(handler, logging.FileHandler)
3224
3225 expected = logging.FileHandler('test.log', 'a')
3226 self.addCleanup(expected.close)
3227 self.assertEqual(handler.stream.mode, expected.stream.mode)
3228 self.assertEqual(handler.stream.name, expected.stream.name)
3229
3230 def test_filemode(self):
3231 logging.basicConfig(filename='test.log', filemode='wb')
3232
3233 handler = logging.root.handlers[0]
3234 expected = logging.FileHandler('test.log', 'wb')
3235 self.addCleanup(expected.close)
3236 self.assertEqual(handler.stream.mode, expected.stream.mode)
3237
3238 def test_stream(self):
3239 stream = io.StringIO()
3240 self.addCleanup(stream.close)
3241 logging.basicConfig(stream=stream)
3242
3243 self.assertEqual(len(logging.root.handlers), 1)
3244 handler = logging.root.handlers[0]
3245 self.assertIsInstance(handler, logging.StreamHandler)
3246 self.assertEqual(handler.stream, stream)
3247
3248 def test_format(self):
3249 logging.basicConfig(format='foo')
3250
3251 formatter = logging.root.handlers[0].formatter
3252 self.assertEqual(formatter._style._fmt, 'foo')
3253
3254 def test_datefmt(self):
3255 logging.basicConfig(datefmt='bar')
3256
3257 formatter = logging.root.handlers[0].formatter
3258 self.assertEqual(formatter.datefmt, 'bar')
3259
3260 def test_style(self):
3261 logging.basicConfig(style='$')
3262
3263 formatter = logging.root.handlers[0].formatter
3264 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3265
3266 def test_level(self):
3267 old_level = logging.root.level
3268 self.addCleanup(lambda: logging.root.setLevel(old_level))
3269
3270 logging.basicConfig(level=57)
3271 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003272 # Test that second call has no effect
3273 logging.basicConfig(level=58)
3274 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003275
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003276 def test_incompatible(self):
3277 assertRaises = self.assertRaises
3278 handlers = [logging.StreamHandler()]
3279 stream = sys.stderr
3280 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3281 stream=stream)
3282 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3283 handlers=handlers)
3284 assertRaises(ValueError, logging.basicConfig, stream=stream,
3285 handlers=handlers)
3286
3287 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003288 handlers = [
3289 logging.StreamHandler(),
3290 logging.StreamHandler(sys.stdout),
3291 logging.StreamHandler(),
3292 ]
3293 f = logging.Formatter()
3294 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003295 logging.basicConfig(handlers=handlers)
3296 self.assertIs(handlers[0], logging.root.handlers[0])
3297 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003298 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003299 self.assertIsNotNone(handlers[0].formatter)
3300 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003301 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003302 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3303
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003304 def _test_log(self, method, level=None):
3305 # logging.root has no handlers so basicConfig should be called
3306 called = []
3307
3308 old_basic_config = logging.basicConfig
3309 def my_basic_config(*a, **kw):
3310 old_basic_config()
3311 old_level = logging.root.level
3312 logging.root.setLevel(100) # avoid having messages in stderr
3313 self.addCleanup(lambda: logging.root.setLevel(old_level))
3314 called.append((a, kw))
3315
3316 patch(self, logging, 'basicConfig', my_basic_config)
3317
3318 log_method = getattr(logging, method)
3319 if level is not None:
3320 log_method(level, "test me")
3321 else:
3322 log_method("test me")
3323
3324 # basicConfig was called with no arguments
3325 self.assertEqual(called, [((), {})])
3326
3327 def test_log(self):
3328 self._test_log('log', logging.WARNING)
3329
3330 def test_debug(self):
3331 self._test_log('debug')
3332
3333 def test_info(self):
3334 self._test_log('info')
3335
3336 def test_warning(self):
3337 self._test_log('warning')
3338
3339 def test_error(self):
3340 self._test_log('error')
3341
3342 def test_critical(self):
3343 self._test_log('critical')
3344
3345
3346class LoggerAdapterTest(unittest.TestCase):
3347
3348 def setUp(self):
3349 super(LoggerAdapterTest, self).setUp()
3350 old_handler_list = logging._handlerList[:]
3351
3352 self.recording = RecordingHandler()
3353 self.logger = logging.root
3354 self.logger.addHandler(self.recording)
3355 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3356 self.addCleanup(self.recording.close)
3357
3358 def cleanup():
3359 logging._handlerList[:] = old_handler_list
3360
3361 self.addCleanup(cleanup)
3362 self.addCleanup(logging.shutdown)
3363 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3364
3365 def test_exception(self):
3366 msg = 'testing exception: %r'
3367 exc = None
3368 try:
3369 assert False
3370 except AssertionError as e:
3371 exc = e
3372 self.adapter.exception(msg, self.recording)
3373
3374 self.assertEqual(len(self.recording.records), 1)
3375 record = self.recording.records[0]
3376 self.assertEqual(record.levelno, logging.ERROR)
3377 self.assertEqual(record.msg, msg)
3378 self.assertEqual(record.args, (self.recording,))
3379 self.assertEqual(record.exc_info,
3380 (exc.__class__, exc, exc.__traceback__))
3381
3382 def test_critical(self):
3383 msg = 'critical test! %r'
3384 self.adapter.critical(msg, self.recording)
3385
3386 self.assertEqual(len(self.recording.records), 1)
3387 record = self.recording.records[0]
3388 self.assertEqual(record.levelno, logging.CRITICAL)
3389 self.assertEqual(record.msg, msg)
3390 self.assertEqual(record.args, (self.recording,))
3391
3392 def test_is_enabled_for(self):
3393 old_disable = self.adapter.logger.manager.disable
3394 self.adapter.logger.manager.disable = 33
3395 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3396 'disable', old_disable))
3397 self.assertFalse(self.adapter.isEnabledFor(32))
3398
3399 def test_has_handlers(self):
3400 self.assertTrue(self.adapter.hasHandlers())
3401
3402 for handler in self.logger.handlers:
3403 self.logger.removeHandler(handler)
3404 assert not self.logger.hasHandlers()
3405
3406 self.assertFalse(self.adapter.hasHandlers())
3407
3408
3409class LoggerTest(BaseTest):
3410
3411 def setUp(self):
3412 super(LoggerTest, self).setUp()
3413 self.recording = RecordingHandler()
3414 self.logger = logging.Logger(name='blah')
3415 self.logger.addHandler(self.recording)
3416 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3417 self.addCleanup(self.recording.close)
3418 self.addCleanup(logging.shutdown)
3419
3420 def test_set_invalid_level(self):
3421 self.assertRaises(TypeError, self.logger.setLevel, object())
3422
3423 def test_exception(self):
3424 msg = 'testing exception: %r'
3425 exc = None
3426 try:
3427 assert False
3428 except AssertionError as e:
3429 exc = e
3430 self.logger.exception(msg, self.recording)
3431
3432 self.assertEqual(len(self.recording.records), 1)
3433 record = self.recording.records[0]
3434 self.assertEqual(record.levelno, logging.ERROR)
3435 self.assertEqual(record.msg, msg)
3436 self.assertEqual(record.args, (self.recording,))
3437 self.assertEqual(record.exc_info,
3438 (exc.__class__, exc, exc.__traceback__))
3439
3440 def test_log_invalid_level_with_raise(self):
3441 old_raise = logging.raiseExceptions
3442 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3443
3444 logging.raiseExceptions = True
3445 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3446
3447 def test_log_invalid_level_no_raise(self):
3448 old_raise = logging.raiseExceptions
3449 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3450
3451 logging.raiseExceptions = False
3452 self.logger.log('10', 'test message') # no exception happens
3453
3454 def test_find_caller_with_stack_info(self):
3455 called = []
3456 patch(self, logging.traceback, 'print_stack',
3457 lambda f, file: called.append(file.getvalue()))
3458
3459 self.logger.findCaller(stack_info=True)
3460
3461 self.assertEqual(len(called), 1)
3462 self.assertEqual('Stack (most recent call last):\n', called[0])
3463
3464 def test_make_record_with_extra_overwrite(self):
3465 name = 'my record'
3466 level = 13
3467 fn = lno = msg = args = exc_info = func = sinfo = None
3468 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3469 exc_info, func, sinfo)
3470
3471 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3472 extra = {key: 'some value'}
3473 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3474 fn, lno, msg, args, exc_info,
3475 extra=extra, sinfo=sinfo)
3476
3477 def test_make_record_with_extra_no_overwrite(self):
3478 name = 'my record'
3479 level = 13
3480 fn = lno = msg = args = exc_info = func = sinfo = None
3481 extra = {'valid_key': 'some value'}
3482 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3483 exc_info, extra=extra, sinfo=sinfo)
3484 self.assertIn('valid_key', result.__dict__)
3485
3486 def test_has_handlers(self):
3487 self.assertTrue(self.logger.hasHandlers())
3488
3489 for handler in self.logger.handlers:
3490 self.logger.removeHandler(handler)
3491 assert not self.logger.hasHandlers()
3492
3493 self.assertFalse(self.logger.hasHandlers())
3494
3495 def test_has_handlers_no_propagate(self):
3496 child_logger = logging.getLogger('blah.child')
3497 child_logger.propagate = False
3498 assert child_logger.handlers == []
3499
3500 self.assertFalse(child_logger.hasHandlers())
3501
3502 def test_is_enabled_for(self):
3503 old_disable = self.logger.manager.disable
3504 self.logger.manager.disable = 23
3505 self.addCleanup(lambda: setattr(self.logger.manager,
3506 'disable', old_disable))
3507 self.assertFalse(self.logger.isEnabledFor(22))
3508
3509
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003510class BaseFileTest(BaseTest):
3511 "Base class for handler tests that write log files"
3512
3513 def setUp(self):
3514 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003515 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3516 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003517 self.rmfiles = []
3518
3519 def tearDown(self):
3520 for fn in self.rmfiles:
3521 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003522 if os.path.exists(self.fn):
3523 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003524 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003525
3526 def assertLogFile(self, filename):
3527 "Assert a log file is there and register it for deletion"
3528 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003529 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003530 self.rmfiles.append(filename)
3531
3532
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003533class FileHandlerTest(BaseFileTest):
3534 def test_delay(self):
3535 os.unlink(self.fn)
3536 fh = logging.FileHandler(self.fn, delay=True)
3537 self.assertIsNone(fh.stream)
3538 self.assertFalse(os.path.exists(self.fn))
3539 fh.handle(logging.makeLogRecord({}))
3540 self.assertIsNotNone(fh.stream)
3541 self.assertTrue(os.path.exists(self.fn))
3542 fh.close()
3543
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003544class RotatingFileHandlerTest(BaseFileTest):
3545 def next_rec(self):
3546 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3547 self.next_message(), None, None, None)
3548
3549 def test_should_not_rollover(self):
3550 # If maxbytes is zero rollover never occurs
3551 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3552 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003553 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003554
3555 def test_should_rollover(self):
3556 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3557 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003558 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003559
3560 def test_file_created(self):
3561 # checks that the file is created and assumes it was created
3562 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003563 rh = logging.handlers.RotatingFileHandler(self.fn)
3564 rh.emit(self.next_rec())
3565 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003566 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003567
3568 def test_rollover_filenames(self):
3569 rh = logging.handlers.RotatingFileHandler(
3570 self.fn, backupCount=2, maxBytes=1)
3571 rh.emit(self.next_rec())
3572 self.assertLogFile(self.fn)
3573 rh.emit(self.next_rec())
3574 self.assertLogFile(self.fn + ".1")
3575 rh.emit(self.next_rec())
3576 self.assertLogFile(self.fn + ".2")
3577 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003578 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003579
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003580class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003581 # other test methods added below
3582 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003583 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3584 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003585 r = logging.makeLogRecord({'msg': 'testing'})
3586 fh.emit(r)
3587 self.assertLogFile(self.fn)
3588 time.sleep(1.0)
3589 fh.emit(r)
3590 fh.close()
3591 # At this point, we should have a recent rotated file which we
3592 # can test for the existence of. However, in practice, on some
3593 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003594 # in time to go to look for the log file. So, we go back a fair
3595 # bit, and stop as soon as we see a rotated file. In theory this
3596 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003597 found = False
3598 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003599 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003600 for secs in range(GO_BACK):
3601 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003602 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3603 found = os.path.exists(fn)
3604 if found:
3605 self.rmfiles.append(fn)
3606 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003607 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3608 if not found:
3609 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003610 dn, fn = os.path.split(self.fn)
3611 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003612 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3613 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajipfd285022011-05-07 17:01:22 +01003614 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003615
Vinay Sajip0372e102011-05-05 12:59:14 +01003616 def test_invalid(self):
3617 assertRaises = self.assertRaises
3618 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003619 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003620 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003621 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003622 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003623 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003624
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003625def secs(**kw):
3626 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3627
3628for when, exp in (('S', 1),
3629 ('M', 60),
3630 ('H', 60 * 60),
3631 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003632 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003633 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003634 ('W0', secs(days=4, hours=24)),
3635 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003636 def test_compute_rollover(self, when=when, exp=exp):
3637 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003638 self.fn, when=when, interval=1, backupCount=0, utc=True)
3639 currentTime = 0.0
3640 actual = rh.computeRollover(currentTime)
3641 if exp != actual:
3642 # Failures occur on some systems for MIDNIGHT and W0.
3643 # Print detailed calculation for MIDNIGHT so we can try to see
3644 # what's going on
3645 import time
3646 if when == 'MIDNIGHT':
3647 try:
3648 if rh.utc:
3649 t = time.gmtime(currentTime)
3650 else:
3651 t = time.localtime(currentTime)
3652 currentHour = t[3]
3653 currentMinute = t[4]
3654 currentSecond = t[5]
3655 # r is the number of seconds left between now and midnight
3656 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3657 currentMinute) * 60 +
3658 currentSecond)
3659 result = currentTime + r
3660 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3661 print('currentHour: %s' % currentHour, file=sys.stderr)
3662 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3663 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3664 print('r: %s' % r, file=sys.stderr)
3665 print('result: %s' % result, file=sys.stderr)
3666 except Exception:
3667 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3668 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003669 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003670 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3671
Vinay Sajip60ccd822011-05-09 17:32:09 +01003672
3673@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3674class NTEventLogHandlerTest(BaseTest):
3675 def test_basic(self):
3676 logtype = 'Application'
3677 elh = win32evtlog.OpenEventLog(None, logtype)
3678 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3679 h = logging.handlers.NTEventLogHandler('test_logging')
3680 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3681 h.handle(r)
3682 h.close()
3683 # Now see if the event is recorded
3684 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3685 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3686 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3687 found = False
3688 GO_BACK = 100
3689 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3690 for e in events:
3691 if e.SourceName != 'test_logging':
3692 continue
3693 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3694 if msg != 'Test Log Message\r\n':
3695 continue
3696 found = True
3697 break
3698 msg = 'Record not found in event log, went back %d records' % GO_BACK
3699 self.assertTrue(found, msg=msg)
3700
Christian Heimes180510d2008-03-03 19:15:45 +00003701# Set the locale to the platform-dependent default. I have no idea
3702# why the test does this, but in any case we save the current locale
3703# first and restore it at the end.
3704@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003705def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003706 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003707 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003708 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3709 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3710 ManagerTest, FormatterTest, BufferingFormatterTest,
3711 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3712 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3713 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3714 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003715 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003716 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003717 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003718 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003719
Christian Heimes180510d2008-03-03 19:15:45 +00003720if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003721 test_main()