blob: e4fc26644261c2b2d4760548f9903e743ae92331 [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 """
Vinay Sajip3ef12292011-05-23 23:00:42 +0100891 def __init__(self, addr, handler, poll_interval=0.5,
892 bind_and_activate=True):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100893 class DelegatingUDPRequestHandler(DatagramRequestHandler):
894
895 def handle(self):
896 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100897
898 def finish(self):
899 data = self.wfile.getvalue()
Vinay Sajip3ef12292011-05-23 23:00:42 +0100900 if data:
901 try:
902 super(DelegatingUDPRequestHandler, self).finish()
903 except socket.error:
904 if not self.server._closed:
905 raise
Vinay Sajipba980db2011-05-23 21:37:54 +0100906
Vinay Sajip3ef12292011-05-23 23:00:42 +0100907 ThreadingUDPServer.__init__(self, addr,
908 DelegatingUDPRequestHandler,
Vinay Sajipce7c9782011-05-17 07:15:53 +0100909 bind_and_activate)
910 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100911 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100912
913 def server_bind(self):
914 super(TestUDPServer, self).server_bind()
915 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100916
Vinay Sajipba980db2011-05-23 21:37:54 +0100917 def server_close(self):
918 super(TestUDPServer, self).server_close()
919 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100920
921# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100922
Vinay Sajipce7c9782011-05-17 07:15:53 +0100923@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100924class SMTPHandlerTest(BaseTest):
925 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100926 sockmap = {}
927 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
928 sockmap)
929 server.start()
930 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100931 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
932 self.assertEqual(h.toaddrs, ['you'])
933 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100934 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100935 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100936 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100937 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100938 server.stop()
939 self.assertEqual(len(self.messages), 1)
940 peer, mailfrom, rcpttos, data = self.messages[0]
941 self.assertEqual(mailfrom, 'me')
942 self.assertEqual(rcpttos, ['you'])
943 self.assertTrue('\nSubject: Log\n' in data)
944 self.assertTrue(data.endswith('\n\nHello'))
945 h.close()
946
947 def process_message(self, *args):
948 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100949 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100950
Christian Heimes180510d2008-03-03 19:15:45 +0000951class MemoryHandlerTest(BaseTest):
952
953 """Tests for the MemoryHandler."""
954
955 # Do not bother with a logger name group.
956 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
957
958 def setUp(self):
959 BaseTest.setUp(self)
960 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
961 self.root_hdlr)
962 self.mem_logger = logging.getLogger('mem')
963 self.mem_logger.propagate = 0
964 self.mem_logger.addHandler(self.mem_hdlr)
965
966 def tearDown(self):
967 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000968 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000969
970 def test_flush(self):
971 # The memory handler flushes to its target handler based on specific
972 # criteria (message count and message level).
973 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000974 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000975 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000976 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000977 # This will flush because the level is >= logging.WARNING
978 self.mem_logger.warn(self.next_message())
979 lines = [
980 ('DEBUG', '1'),
981 ('INFO', '2'),
982 ('WARNING', '3'),
983 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000984 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000985 for n in (4, 14):
986 for i in range(9):
987 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000988 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000989 # This will flush because it's the 10th message since the last
990 # flush.
991 self.mem_logger.debug(self.next_message())
992 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000993 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000994
995 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000996 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000997
998
999class ExceptionFormatter(logging.Formatter):
1000 """A special exception formatter."""
1001 def formatException(self, ei):
1002 return "Got a [%s]" % ei[0].__name__
1003
1004
1005class ConfigFileTest(BaseTest):
1006
1007 """Reading logging config from a .ini-style config file."""
1008
1009 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1010
1011 # config0 is a standard configuration.
1012 config0 = """
1013 [loggers]
1014 keys=root
1015
1016 [handlers]
1017 keys=hand1
1018
1019 [formatters]
1020 keys=form1
1021
1022 [logger_root]
1023 level=WARNING
1024 handlers=hand1
1025
1026 [handler_hand1]
1027 class=StreamHandler
1028 level=NOTSET
1029 formatter=form1
1030 args=(sys.stdout,)
1031
1032 [formatter_form1]
1033 format=%(levelname)s ++ %(message)s
1034 datefmt=
1035 """
1036
1037 # config1 adds a little to the standard configuration.
1038 config1 = """
1039 [loggers]
1040 keys=root,parser
1041
1042 [handlers]
1043 keys=hand1
1044
1045 [formatters]
1046 keys=form1
1047
1048 [logger_root]
1049 level=WARNING
1050 handlers=
1051
1052 [logger_parser]
1053 level=DEBUG
1054 handlers=hand1
1055 propagate=1
1056 qualname=compiler.parser
1057
1058 [handler_hand1]
1059 class=StreamHandler
1060 level=NOTSET
1061 formatter=form1
1062 args=(sys.stdout,)
1063
1064 [formatter_form1]
1065 format=%(levelname)s ++ %(message)s
1066 datefmt=
1067 """
1068
Vinay Sajip3f84b072011-03-07 17:49:33 +00001069 # config1a moves the handler to the root.
1070 config1a = """
1071 [loggers]
1072 keys=root,parser
1073
1074 [handlers]
1075 keys=hand1
1076
1077 [formatters]
1078 keys=form1
1079
1080 [logger_root]
1081 level=WARNING
1082 handlers=hand1
1083
1084 [logger_parser]
1085 level=DEBUG
1086 handlers=
1087 propagate=1
1088 qualname=compiler.parser
1089
1090 [handler_hand1]
1091 class=StreamHandler
1092 level=NOTSET
1093 formatter=form1
1094 args=(sys.stdout,)
1095
1096 [formatter_form1]
1097 format=%(levelname)s ++ %(message)s
1098 datefmt=
1099 """
1100
Christian Heimes180510d2008-03-03 19:15:45 +00001101 # config2 has a subtle configuration error that should be reported
1102 config2 = config1.replace("sys.stdout", "sys.stbout")
1103
1104 # config3 has a less subtle configuration error
1105 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1106
1107 # config4 specifies a custom formatter class to be loaded
1108 config4 = """
1109 [loggers]
1110 keys=root
1111
1112 [handlers]
1113 keys=hand1
1114
1115 [formatters]
1116 keys=form1
1117
1118 [logger_root]
1119 level=NOTSET
1120 handlers=hand1
1121
1122 [handler_hand1]
1123 class=StreamHandler
1124 level=NOTSET
1125 formatter=form1
1126 args=(sys.stdout,)
1127
1128 [formatter_form1]
1129 class=""" + __name__ + """.ExceptionFormatter
1130 format=%(levelname)s:%(name)s:%(message)s
1131 datefmt=
1132 """
1133
Georg Brandl3dbca812008-07-23 16:10:53 +00001134 # config5 specifies a custom handler class to be loaded
1135 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1136
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001137 # config6 uses ', ' delimiters in the handlers and formatters sections
1138 config6 = """
1139 [loggers]
1140 keys=root,parser
1141
1142 [handlers]
1143 keys=hand1, hand2
1144
1145 [formatters]
1146 keys=form1, form2
1147
1148 [logger_root]
1149 level=WARNING
1150 handlers=
1151
1152 [logger_parser]
1153 level=DEBUG
1154 handlers=hand1
1155 propagate=1
1156 qualname=compiler.parser
1157
1158 [handler_hand1]
1159 class=StreamHandler
1160 level=NOTSET
1161 formatter=form1
1162 args=(sys.stdout,)
1163
1164 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001165 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001166 level=NOTSET
1167 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001168 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001169
1170 [formatter_form1]
1171 format=%(levelname)s ++ %(message)s
1172 datefmt=
1173
1174 [formatter_form2]
1175 format=%(message)s
1176 datefmt=
1177 """
1178
Vinay Sajip3f84b072011-03-07 17:49:33 +00001179 # config7 adds a compiler logger.
1180 config7 = """
1181 [loggers]
1182 keys=root,parser,compiler
1183
1184 [handlers]
1185 keys=hand1
1186
1187 [formatters]
1188 keys=form1
1189
1190 [logger_root]
1191 level=WARNING
1192 handlers=hand1
1193
1194 [logger_compiler]
1195 level=DEBUG
1196 handlers=
1197 propagate=1
1198 qualname=compiler
1199
1200 [logger_parser]
1201 level=DEBUG
1202 handlers=
1203 propagate=1
1204 qualname=compiler.parser
1205
1206 [handler_hand1]
1207 class=StreamHandler
1208 level=NOTSET
1209 formatter=form1
1210 args=(sys.stdout,)
1211
1212 [formatter_form1]
1213 format=%(levelname)s ++ %(message)s
1214 datefmt=
1215 """
1216
Christian Heimes180510d2008-03-03 19:15:45 +00001217 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001218 file = io.StringIO(textwrap.dedent(conf))
1219 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001220
1221 def test_config0_ok(self):
1222 # A simple config file which overrides the default settings.
1223 with captured_stdout() as output:
1224 self.apply_config(self.config0)
1225 logger = logging.getLogger()
1226 # Won't output anything
1227 logger.info(self.next_message())
1228 # Outputs a message
1229 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001230 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001231 ('ERROR', '2'),
1232 ], stream=output)
1233 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001234 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001235
Georg Brandl3dbca812008-07-23 16:10:53 +00001236 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001237 # A config file defining a sub-parser as well.
1238 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001239 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001240 logger = logging.getLogger("compiler.parser")
1241 # Both will output a message
1242 logger.info(self.next_message())
1243 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001244 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001245 ('INFO', '1'),
1246 ('ERROR', '2'),
1247 ], stream=output)
1248 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001249 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001250
1251 def test_config2_failure(self):
1252 # A simple config file which overrides the default settings.
1253 self.assertRaises(Exception, self.apply_config, self.config2)
1254
1255 def test_config3_failure(self):
1256 # A simple config file which overrides the default settings.
1257 self.assertRaises(Exception, self.apply_config, self.config3)
1258
1259 def test_config4_ok(self):
1260 # A config file specifying a custom formatter class.
1261 with captured_stdout() as output:
1262 self.apply_config(self.config4)
1263 logger = logging.getLogger()
1264 try:
1265 raise RuntimeError()
1266 except RuntimeError:
1267 logging.exception("just testing")
1268 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001269 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001270 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1271 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001272 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001273
Georg Brandl3dbca812008-07-23 16:10:53 +00001274 def test_config5_ok(self):
1275 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001276
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001277 def test_config6_ok(self):
1278 self.test_config1_ok(config=self.config6)
1279
Vinay Sajip3f84b072011-03-07 17:49:33 +00001280 def test_config7_ok(self):
1281 with captured_stdout() as output:
1282 self.apply_config(self.config1a)
1283 logger = logging.getLogger("compiler.parser")
1284 # See issue #11424. compiler-hyphenated sorts
1285 # between compiler and compiler.xyz and this
1286 # was preventing compiler.xyz from being included
1287 # in the child loggers of compiler because of an
1288 # overzealous loop termination condition.
1289 hyphenated = logging.getLogger('compiler-hyphenated')
1290 # All will output a message
1291 logger.info(self.next_message())
1292 logger.error(self.next_message())
1293 hyphenated.critical(self.next_message())
1294 self.assert_log_lines([
1295 ('INFO', '1'),
1296 ('ERROR', '2'),
1297 ('CRITICAL', '3'),
1298 ], stream=output)
1299 # Original logger output is empty.
1300 self.assert_log_lines([])
1301 with captured_stdout() as output:
1302 self.apply_config(self.config7)
1303 logger = logging.getLogger("compiler.parser")
1304 self.assertFalse(logger.disabled)
1305 # Both will output a message
1306 logger.info(self.next_message())
1307 logger.error(self.next_message())
1308 logger = logging.getLogger("compiler.lexer")
1309 # Both will output a message
1310 logger.info(self.next_message())
1311 logger.error(self.next_message())
1312 # Will not appear
1313 hyphenated.critical(self.next_message())
1314 self.assert_log_lines([
1315 ('INFO', '4'),
1316 ('ERROR', '5'),
1317 ('INFO', '6'),
1318 ('ERROR', '7'),
1319 ], stream=output)
1320 # Original logger output is empty.
1321 self.assert_log_lines([])
1322
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001323
Victor Stinner45df8202010-04-28 22:31:17 +00001324@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001325class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001326
Christian Heimes180510d2008-03-03 19:15:45 +00001327 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001328
Christian Heimes180510d2008-03-03 19:15:45 +00001329 def setUp(self):
1330 """Set up a TCP server to receive log messages, and a SocketHandler
1331 pointing to that server's address and port."""
1332 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001333 addr = ('localhost', 0)
1334 self.server = server = TestTCPServer(addr, self.handle_socket,
1335 0.01)
1336 server.start()
1337 server.ready.wait()
1338 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1339 server.port)
1340 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001341 self.root_logger.removeHandler(self.root_logger.handlers[0])
1342 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001343 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001344
Christian Heimes180510d2008-03-03 19:15:45 +00001345 def tearDown(self):
1346 """Shutdown the TCP server."""
1347 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001348 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001349 self.root_logger.removeHandler(self.sock_hdlr)
1350 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001351 finally:
1352 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001353
Vinay Sajip7367d082011-05-02 13:17:27 +01001354 def handle_socket(self, request):
1355 conn = request.connection
1356 while True:
1357 chunk = conn.recv(4)
1358 if len(chunk) < 4:
1359 break
1360 slen = struct.unpack(">L", chunk)[0]
1361 chunk = conn.recv(slen)
1362 while len(chunk) < slen:
1363 chunk = chunk + conn.recv(slen - len(chunk))
1364 obj = pickle.loads(chunk)
1365 record = logging.makeLogRecord(obj)
1366 self.log_output += record.msg + '\n'
1367 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001368
Christian Heimes180510d2008-03-03 19:15:45 +00001369 def test_output(self):
1370 # The log message sent to the SocketHandler is properly received.
1371 logger = logging.getLogger("tcp")
1372 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001373 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001374 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001375 self.handled.acquire()
1376 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001377
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001378 def test_noserver(self):
1379 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001380 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001381 #The logging call should try to connect, which should fail
1382 try:
1383 raise RuntimeError('Deliberate mistake')
1384 except RuntimeError:
1385 self.root_logger.exception('Never sent')
1386 self.root_logger.error('Never sent, either')
1387 now = time.time()
1388 self.assertTrue(self.sock_hdlr.retryTime > now)
1389 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1390 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001391
Vinay Sajip7367d082011-05-02 13:17:27 +01001392
1393@unittest.skipUnless(threading, 'Threading required for this test.')
1394class DatagramHandlerTest(BaseTest):
1395
1396 """Test for DatagramHandler."""
1397
1398 def setUp(self):
1399 """Set up a UDP server to receive log messages, and a DatagramHandler
1400 pointing to that server's address and port."""
1401 BaseTest.setUp(self)
1402 addr = ('localhost', 0)
1403 self.server = server = TestUDPServer(addr, self.handle_datagram,
1404 0.01)
1405 server.start()
1406 server.ready.wait()
1407 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1408 server.port)
1409 self.log_output = ''
1410 self.root_logger.removeHandler(self.root_logger.handlers[0])
1411 self.root_logger.addHandler(self.sock_hdlr)
1412 self.handled = threading.Event()
1413
1414 def tearDown(self):
1415 """Shutdown the UDP server."""
1416 try:
1417 self.server.stop(2.0)
1418 self.root_logger.removeHandler(self.sock_hdlr)
1419 self.sock_hdlr.close()
1420 finally:
1421 BaseTest.tearDown(self)
1422
1423 def handle_datagram(self, request):
1424 slen = struct.pack('>L', 0) # length of prefix
1425 packet = request.packet[len(slen):]
1426 obj = pickle.loads(packet)
1427 record = logging.makeLogRecord(obj)
1428 self.log_output += record.msg + '\n'
1429 self.handled.set()
1430
1431 def test_output(self):
1432 # The log message sent to the DatagramHandler is properly received.
1433 logger = logging.getLogger("udp")
1434 logger.error("spam")
1435 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001436 self.handled.clear()
1437 logger.error("eggs")
1438 self.handled.wait()
1439 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001440
1441
1442@unittest.skipUnless(threading, 'Threading required for this test.')
1443class SysLogHandlerTest(BaseTest):
1444
1445 """Test for SysLogHandler using UDP."""
1446
1447 def setUp(self):
1448 """Set up a UDP server to receive log messages, and a SysLogHandler
1449 pointing to that server's address and port."""
1450 BaseTest.setUp(self)
1451 addr = ('localhost', 0)
1452 self.server = server = TestUDPServer(addr, self.handle_datagram,
1453 0.01)
1454 server.start()
1455 server.ready.wait()
1456 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1457 server.port))
1458 self.log_output = ''
1459 self.root_logger.removeHandler(self.root_logger.handlers[0])
1460 self.root_logger.addHandler(self.sl_hdlr)
1461 self.handled = threading.Event()
1462
1463 def tearDown(self):
1464 """Shutdown the UDP server."""
1465 try:
1466 self.server.stop(2.0)
1467 self.root_logger.removeHandler(self.sl_hdlr)
1468 self.sl_hdlr.close()
1469 finally:
1470 BaseTest.tearDown(self)
1471
1472 def handle_datagram(self, request):
1473 self.log_output = request.packet
1474 self.handled.set()
1475
1476 def test_output(self):
1477 # The log message sent to the SysLogHandler is properly received.
1478 logger = logging.getLogger("slh")
1479 logger.error("sp\xe4m")
1480 self.handled.wait()
1481 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1482
1483
1484@unittest.skipUnless(threading, 'Threading required for this test.')
1485class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001486 """Test for HTTPHandler."""
1487
Vinay Sajip9ba87612011-05-21 11:32:15 +01001488 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1489MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1490zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1491aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1492AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
14930tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
14945LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1495piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
14962z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1497i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1498PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1499E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1500rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1501/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1502-----END RSA PRIVATE KEY-----
1503-----BEGIN CERTIFICATE-----
1504MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1505BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1506EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1507xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1508l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1509vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1510BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1511S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1512BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15134f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15141c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1515IRbTpvlHWPjsSvHz0ZOH
1516-----END CERTIFICATE-----"""
1517
Vinay Sajip7367d082011-05-02 13:17:27 +01001518 def setUp(self):
1519 """Set up an HTTP server to receive log messages, and a HTTPHandler
1520 pointing to that server's address and port."""
1521 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001522 self.handled = threading.Event()
1523
Vinay Sajip7367d082011-05-02 13:17:27 +01001524 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001525 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001526 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001527 if self.command == 'POST':
1528 try:
1529 rlen = int(request.headers['Content-Length'])
1530 self.post_data = request.rfile.read(rlen)
1531 except:
1532 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001533 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001534 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001535 self.handled.set()
1536
1537 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001538 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001539 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001540 root_logger = self.root_logger
1541 root_logger.removeHandler(self.root_logger.handlers[0])
1542 for secure in (False, True):
1543 addr = ('localhost', 0)
1544 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001545 try:
1546 import ssl
1547 fd, fn = tempfile.mkstemp()
1548 os.close(fd)
1549 with open(fn, 'w') as f:
1550 f.write(self.PEMFILE)
1551 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1552 sslctx.load_cert_chain(fn)
1553 os.unlink(fn)
1554 except ImportError:
1555 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001556 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001557 sslctx = None
1558 self.server = server = TestHTTPServer(addr, self.handle_request,
1559 0.01, sslctx=sslctx)
1560 server.start()
1561 server.ready.wait()
1562 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001563 secure_client = secure and sslctx
1564 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1565 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001566 self.log_data = None
1567 root_logger.addHandler(self.h_hdlr)
1568
1569 for method in ('GET', 'POST'):
1570 self.h_hdlr.method = method
1571 self.handled.clear()
1572 msg = "sp\xe4m"
1573 logger.error(msg)
1574 self.handled.wait()
1575 self.assertEqual(self.log_data.path, '/frob')
1576 self.assertEqual(self.command, method)
1577 if method == 'GET':
1578 d = parse_qs(self.log_data.query)
1579 else:
1580 d = parse_qs(self.post_data.decode('utf-8'))
1581 self.assertEqual(d['name'], ['http'])
1582 self.assertEqual(d['funcName'], ['test_output'])
1583 self.assertEqual(d['msg'], [msg])
1584
1585 self.server.stop(2.0)
1586 self.root_logger.removeHandler(self.h_hdlr)
1587 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001588
Christian Heimes180510d2008-03-03 19:15:45 +00001589class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001590
Christian Heimes180510d2008-03-03 19:15:45 +00001591 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001592
Christian Heimes180510d2008-03-03 19:15:45 +00001593 def setUp(self):
1594 """Create a dict to remember potentially destroyed objects."""
1595 BaseTest.setUp(self)
1596 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001597
Christian Heimes180510d2008-03-03 19:15:45 +00001598 def _watch_for_survival(self, *args):
1599 """Watch the given objects for survival, by creating weakrefs to
1600 them."""
1601 for obj in args:
1602 key = id(obj), repr(obj)
1603 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001604
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001605 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001606 """Assert that all objects watched for survival have survived."""
1607 # Trigger cycle breaking.
1608 gc.collect()
1609 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001610 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001611 if ref() is None:
1612 dead.append(repr_)
1613 if dead:
1614 self.fail("%d objects should have survived "
1615 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001616
Christian Heimes180510d2008-03-03 19:15:45 +00001617 def test_persistent_loggers(self):
1618 # Logger objects are persistent and retain their configuration, even
1619 # if visible references are destroyed.
1620 self.root_logger.setLevel(logging.INFO)
1621 foo = logging.getLogger("foo")
1622 self._watch_for_survival(foo)
1623 foo.setLevel(logging.DEBUG)
1624 self.root_logger.debug(self.next_message())
1625 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001626 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001627 ('foo', 'DEBUG', '2'),
1628 ])
1629 del foo
1630 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001631 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001632 # foo has retained its settings.
1633 bar = logging.getLogger("foo")
1634 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001635 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001636 ('foo', 'DEBUG', '2'),
1637 ('foo', 'DEBUG', '3'),
1638 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001639
Benjamin Petersonf91df042009-02-13 02:50:59 +00001640
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001641class EncodingTest(BaseTest):
1642 def test_encoding_plain_file(self):
1643 # In Python 2.x, a plain file object is treated as having no encoding.
1644 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001645 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1646 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001647 # the non-ascii data we write to the log.
1648 data = "foo\x80"
1649 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001650 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001651 log.addHandler(handler)
1652 try:
1653 # write non-ascii data to the log.
1654 log.warning(data)
1655 finally:
1656 log.removeHandler(handler)
1657 handler.close()
1658 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001659 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001660 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001661 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001662 finally:
1663 f.close()
1664 finally:
1665 if os.path.isfile(fn):
1666 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001667
Benjamin Petersonf91df042009-02-13 02:50:59 +00001668 def test_encoding_cyrillic_unicode(self):
1669 log = logging.getLogger("test")
1670 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1671 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1672 #Ensure it's written in a Cyrillic encoding
1673 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001674 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001675 stream = io.BytesIO()
1676 writer = writer_class(stream, 'strict')
1677 handler = logging.StreamHandler(writer)
1678 log.addHandler(handler)
1679 try:
1680 log.warning(message)
1681 finally:
1682 log.removeHandler(handler)
1683 handler.close()
1684 # check we wrote exactly those bytes, ignoring trailing \n etc
1685 s = stream.getvalue()
1686 #Compare against what the data should be when encoded in CP-1251
1687 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1688
1689
Georg Brandlf9734072008-12-07 15:30:06 +00001690class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001691
Georg Brandlf9734072008-12-07 15:30:06 +00001692 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001693 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001694 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001695 self.addCleanup(lambda: logging.captureWarnings(False))
1696 warnings.filterwarnings("always", category=UserWarning)
1697 stream = io.StringIO()
1698 h = logging.StreamHandler(stream)
1699 logger = logging.getLogger("py.warnings")
1700 logger.addHandler(h)
1701 warnings.warn("I'm warning you...")
1702 logger.removeHandler(h)
1703 s = stream.getvalue()
1704 h.close()
1705 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001706
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001707 #See if an explicit file uses the original implementation
1708 a_file = io.StringIO()
1709 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1710 a_file, "Dummy line")
1711 s = a_file.getvalue()
1712 a_file.close()
1713 self.assertEqual(s,
1714 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1715
1716 def test_warnings_no_handlers(self):
1717 with warnings.catch_warnings():
1718 logging.captureWarnings(True)
1719 self.addCleanup(lambda: logging.captureWarnings(False))
1720
1721 # confirm our assumption: no loggers are set
1722 logger = logging.getLogger("py.warnings")
1723 assert logger.handlers == []
1724
1725 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1726 self.assertTrue(len(logger.handlers) == 1)
1727 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001728
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001729
1730def formatFunc(format, datefmt=None):
1731 return logging.Formatter(format, datefmt)
1732
1733def handlerFunc():
1734 return logging.StreamHandler()
1735
1736class CustomHandler(logging.StreamHandler):
1737 pass
1738
1739class ConfigDictTest(BaseTest):
1740
1741 """Reading logging config from a dictionary."""
1742
1743 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1744
1745 # config0 is a standard configuration.
1746 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001747 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001748 'formatters': {
1749 'form1' : {
1750 'format' : '%(levelname)s ++ %(message)s',
1751 },
1752 },
1753 'handlers' : {
1754 'hand1' : {
1755 'class' : 'logging.StreamHandler',
1756 'formatter' : 'form1',
1757 'level' : 'NOTSET',
1758 'stream' : 'ext://sys.stdout',
1759 },
1760 },
1761 'root' : {
1762 'level' : 'WARNING',
1763 'handlers' : ['hand1'],
1764 },
1765 }
1766
1767 # config1 adds a little to the standard configuration.
1768 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001769 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001770 'formatters': {
1771 'form1' : {
1772 'format' : '%(levelname)s ++ %(message)s',
1773 },
1774 },
1775 'handlers' : {
1776 'hand1' : {
1777 'class' : 'logging.StreamHandler',
1778 'formatter' : 'form1',
1779 'level' : 'NOTSET',
1780 'stream' : 'ext://sys.stdout',
1781 },
1782 },
1783 'loggers' : {
1784 'compiler.parser' : {
1785 'level' : 'DEBUG',
1786 'handlers' : ['hand1'],
1787 },
1788 },
1789 'root' : {
1790 'level' : 'WARNING',
1791 },
1792 }
1793
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001794 # config1a moves the handler to the root. Used with config8a
1795 config1a = {
1796 'version': 1,
1797 'formatters': {
1798 'form1' : {
1799 'format' : '%(levelname)s ++ %(message)s',
1800 },
1801 },
1802 'handlers' : {
1803 'hand1' : {
1804 'class' : 'logging.StreamHandler',
1805 'formatter' : 'form1',
1806 'level' : 'NOTSET',
1807 'stream' : 'ext://sys.stdout',
1808 },
1809 },
1810 'loggers' : {
1811 'compiler.parser' : {
1812 'level' : 'DEBUG',
1813 },
1814 },
1815 'root' : {
1816 'level' : 'WARNING',
1817 'handlers' : ['hand1'],
1818 },
1819 }
1820
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001821 # config2 has a subtle configuration error that should be reported
1822 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001823 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001824 'formatters': {
1825 'form1' : {
1826 'format' : '%(levelname)s ++ %(message)s',
1827 },
1828 },
1829 'handlers' : {
1830 'hand1' : {
1831 'class' : 'logging.StreamHandler',
1832 'formatter' : 'form1',
1833 'level' : 'NOTSET',
1834 'stream' : 'ext://sys.stdbout',
1835 },
1836 },
1837 'loggers' : {
1838 'compiler.parser' : {
1839 'level' : 'DEBUG',
1840 'handlers' : ['hand1'],
1841 },
1842 },
1843 'root' : {
1844 'level' : 'WARNING',
1845 },
1846 }
1847
1848 #As config1 but with a misspelt level on a handler
1849 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001850 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001851 'formatters': {
1852 'form1' : {
1853 'format' : '%(levelname)s ++ %(message)s',
1854 },
1855 },
1856 'handlers' : {
1857 'hand1' : {
1858 'class' : 'logging.StreamHandler',
1859 'formatter' : 'form1',
1860 'level' : 'NTOSET',
1861 'stream' : 'ext://sys.stdout',
1862 },
1863 },
1864 'loggers' : {
1865 'compiler.parser' : {
1866 'level' : 'DEBUG',
1867 'handlers' : ['hand1'],
1868 },
1869 },
1870 'root' : {
1871 'level' : 'WARNING',
1872 },
1873 }
1874
1875
1876 #As config1 but with a misspelt level on a logger
1877 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001878 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001879 'formatters': {
1880 'form1' : {
1881 'format' : '%(levelname)s ++ %(message)s',
1882 },
1883 },
1884 'handlers' : {
1885 'hand1' : {
1886 'class' : 'logging.StreamHandler',
1887 'formatter' : 'form1',
1888 'level' : 'NOTSET',
1889 'stream' : 'ext://sys.stdout',
1890 },
1891 },
1892 'loggers' : {
1893 'compiler.parser' : {
1894 'level' : 'DEBUG',
1895 'handlers' : ['hand1'],
1896 },
1897 },
1898 'root' : {
1899 'level' : 'WRANING',
1900 },
1901 }
1902
1903 # config3 has a less subtle configuration error
1904 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001905 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001906 'formatters': {
1907 'form1' : {
1908 'format' : '%(levelname)s ++ %(message)s',
1909 },
1910 },
1911 'handlers' : {
1912 'hand1' : {
1913 'class' : 'logging.StreamHandler',
1914 'formatter' : 'misspelled_name',
1915 'level' : 'NOTSET',
1916 'stream' : 'ext://sys.stdout',
1917 },
1918 },
1919 'loggers' : {
1920 'compiler.parser' : {
1921 'level' : 'DEBUG',
1922 'handlers' : ['hand1'],
1923 },
1924 },
1925 'root' : {
1926 'level' : 'WARNING',
1927 },
1928 }
1929
1930 # config4 specifies a custom formatter class to be loaded
1931 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001932 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001933 'formatters': {
1934 'form1' : {
1935 '()' : __name__ + '.ExceptionFormatter',
1936 'format' : '%(levelname)s:%(name)s:%(message)s',
1937 },
1938 },
1939 'handlers' : {
1940 'hand1' : {
1941 'class' : 'logging.StreamHandler',
1942 'formatter' : 'form1',
1943 'level' : 'NOTSET',
1944 'stream' : 'ext://sys.stdout',
1945 },
1946 },
1947 'root' : {
1948 'level' : 'NOTSET',
1949 'handlers' : ['hand1'],
1950 },
1951 }
1952
1953 # As config4 but using an actual callable rather than a string
1954 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001955 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001956 'formatters': {
1957 'form1' : {
1958 '()' : ExceptionFormatter,
1959 'format' : '%(levelname)s:%(name)s:%(message)s',
1960 },
1961 'form2' : {
1962 '()' : __name__ + '.formatFunc',
1963 'format' : '%(levelname)s:%(name)s:%(message)s',
1964 },
1965 'form3' : {
1966 '()' : formatFunc,
1967 'format' : '%(levelname)s:%(name)s:%(message)s',
1968 },
1969 },
1970 'handlers' : {
1971 'hand1' : {
1972 'class' : 'logging.StreamHandler',
1973 'formatter' : 'form1',
1974 'level' : 'NOTSET',
1975 'stream' : 'ext://sys.stdout',
1976 },
1977 'hand2' : {
1978 '()' : handlerFunc,
1979 },
1980 },
1981 'root' : {
1982 'level' : 'NOTSET',
1983 'handlers' : ['hand1'],
1984 },
1985 }
1986
1987 # config5 specifies a custom handler class to be loaded
1988 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001989 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001990 'formatters': {
1991 'form1' : {
1992 'format' : '%(levelname)s ++ %(message)s',
1993 },
1994 },
1995 'handlers' : {
1996 'hand1' : {
1997 'class' : __name__ + '.CustomHandler',
1998 'formatter' : 'form1',
1999 'level' : 'NOTSET',
2000 'stream' : 'ext://sys.stdout',
2001 },
2002 },
2003 'loggers' : {
2004 'compiler.parser' : {
2005 'level' : 'DEBUG',
2006 'handlers' : ['hand1'],
2007 },
2008 },
2009 'root' : {
2010 'level' : 'WARNING',
2011 },
2012 }
2013
2014 # config6 specifies a custom handler class to be loaded
2015 # but has bad arguments
2016 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002017 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002018 'formatters': {
2019 'form1' : {
2020 'format' : '%(levelname)s ++ %(message)s',
2021 },
2022 },
2023 'handlers' : {
2024 'hand1' : {
2025 'class' : __name__ + '.CustomHandler',
2026 'formatter' : 'form1',
2027 'level' : 'NOTSET',
2028 'stream' : 'ext://sys.stdout',
2029 '9' : 'invalid parameter name',
2030 },
2031 },
2032 'loggers' : {
2033 'compiler.parser' : {
2034 'level' : 'DEBUG',
2035 'handlers' : ['hand1'],
2036 },
2037 },
2038 'root' : {
2039 'level' : 'WARNING',
2040 },
2041 }
2042
2043 #config 7 does not define compiler.parser but defines compiler.lexer
2044 #so compiler.parser should be disabled after applying it
2045 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002046 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002047 'formatters': {
2048 'form1' : {
2049 'format' : '%(levelname)s ++ %(message)s',
2050 },
2051 },
2052 'handlers' : {
2053 'hand1' : {
2054 'class' : 'logging.StreamHandler',
2055 'formatter' : 'form1',
2056 'level' : 'NOTSET',
2057 'stream' : 'ext://sys.stdout',
2058 },
2059 },
2060 'loggers' : {
2061 'compiler.lexer' : {
2062 'level' : 'DEBUG',
2063 'handlers' : ['hand1'],
2064 },
2065 },
2066 'root' : {
2067 'level' : 'WARNING',
2068 },
2069 }
2070
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002071 # config8 defines both compiler and compiler.lexer
2072 # so compiler.parser should not be disabled (since
2073 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002074 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002075 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002076 'disable_existing_loggers' : False,
2077 'formatters': {
2078 'form1' : {
2079 'format' : '%(levelname)s ++ %(message)s',
2080 },
2081 },
2082 'handlers' : {
2083 'hand1' : {
2084 'class' : 'logging.StreamHandler',
2085 'formatter' : 'form1',
2086 'level' : 'NOTSET',
2087 'stream' : 'ext://sys.stdout',
2088 },
2089 },
2090 'loggers' : {
2091 'compiler' : {
2092 'level' : 'DEBUG',
2093 'handlers' : ['hand1'],
2094 },
2095 'compiler.lexer' : {
2096 },
2097 },
2098 'root' : {
2099 'level' : 'WARNING',
2100 },
2101 }
2102
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002103 # config8a disables existing loggers
2104 config8a = {
2105 'version': 1,
2106 'disable_existing_loggers' : True,
2107 'formatters': {
2108 'form1' : {
2109 'format' : '%(levelname)s ++ %(message)s',
2110 },
2111 },
2112 'handlers' : {
2113 'hand1' : {
2114 'class' : 'logging.StreamHandler',
2115 'formatter' : 'form1',
2116 'level' : 'NOTSET',
2117 'stream' : 'ext://sys.stdout',
2118 },
2119 },
2120 'loggers' : {
2121 'compiler' : {
2122 'level' : 'DEBUG',
2123 'handlers' : ['hand1'],
2124 },
2125 'compiler.lexer' : {
2126 },
2127 },
2128 'root' : {
2129 'level' : 'WARNING',
2130 },
2131 }
2132
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002133 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002134 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002135 'formatters': {
2136 'form1' : {
2137 'format' : '%(levelname)s ++ %(message)s',
2138 },
2139 },
2140 'handlers' : {
2141 'hand1' : {
2142 'class' : 'logging.StreamHandler',
2143 'formatter' : 'form1',
2144 'level' : 'WARNING',
2145 'stream' : 'ext://sys.stdout',
2146 },
2147 },
2148 'loggers' : {
2149 'compiler.parser' : {
2150 'level' : 'WARNING',
2151 'handlers' : ['hand1'],
2152 },
2153 },
2154 'root' : {
2155 'level' : 'NOTSET',
2156 },
2157 }
2158
2159 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002160 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002161 'incremental' : True,
2162 'handlers' : {
2163 'hand1' : {
2164 'level' : 'WARNING',
2165 },
2166 },
2167 'loggers' : {
2168 'compiler.parser' : {
2169 'level' : 'INFO',
2170 },
2171 },
2172 }
2173
2174 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002175 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002176 'incremental' : True,
2177 'handlers' : {
2178 'hand1' : {
2179 'level' : 'INFO',
2180 },
2181 },
2182 'loggers' : {
2183 'compiler.parser' : {
2184 'level' : 'INFO',
2185 },
2186 },
2187 }
2188
2189 #As config1 but with a filter added
2190 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002191 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002192 'formatters': {
2193 'form1' : {
2194 'format' : '%(levelname)s ++ %(message)s',
2195 },
2196 },
2197 'filters' : {
2198 'filt1' : {
2199 'name' : 'compiler.parser',
2200 },
2201 },
2202 'handlers' : {
2203 'hand1' : {
2204 'class' : 'logging.StreamHandler',
2205 'formatter' : 'form1',
2206 'level' : 'NOTSET',
2207 'stream' : 'ext://sys.stdout',
2208 'filters' : ['filt1'],
2209 },
2210 },
2211 'loggers' : {
2212 'compiler.parser' : {
2213 'level' : 'DEBUG',
2214 'filters' : ['filt1'],
2215 },
2216 },
2217 'root' : {
2218 'level' : 'WARNING',
2219 'handlers' : ['hand1'],
2220 },
2221 }
2222
2223 #As config1 but using cfg:// references
2224 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002225 'version': 1,
2226 'true_formatters': {
2227 'form1' : {
2228 'format' : '%(levelname)s ++ %(message)s',
2229 },
2230 },
2231 'handler_configs': {
2232 'hand1' : {
2233 'class' : 'logging.StreamHandler',
2234 'formatter' : 'form1',
2235 'level' : 'NOTSET',
2236 'stream' : 'ext://sys.stdout',
2237 },
2238 },
2239 'formatters' : 'cfg://true_formatters',
2240 'handlers' : {
2241 'hand1' : 'cfg://handler_configs[hand1]',
2242 },
2243 'loggers' : {
2244 'compiler.parser' : {
2245 'level' : 'DEBUG',
2246 'handlers' : ['hand1'],
2247 },
2248 },
2249 'root' : {
2250 'level' : 'WARNING',
2251 },
2252 }
2253
2254 #As config11 but missing the version key
2255 config12 = {
2256 'true_formatters': {
2257 'form1' : {
2258 'format' : '%(levelname)s ++ %(message)s',
2259 },
2260 },
2261 'handler_configs': {
2262 'hand1' : {
2263 'class' : 'logging.StreamHandler',
2264 'formatter' : 'form1',
2265 'level' : 'NOTSET',
2266 'stream' : 'ext://sys.stdout',
2267 },
2268 },
2269 'formatters' : 'cfg://true_formatters',
2270 'handlers' : {
2271 'hand1' : 'cfg://handler_configs[hand1]',
2272 },
2273 'loggers' : {
2274 'compiler.parser' : {
2275 'level' : 'DEBUG',
2276 'handlers' : ['hand1'],
2277 },
2278 },
2279 'root' : {
2280 'level' : 'WARNING',
2281 },
2282 }
2283
2284 #As config11 but using an unsupported version
2285 config13 = {
2286 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002287 'true_formatters': {
2288 'form1' : {
2289 'format' : '%(levelname)s ++ %(message)s',
2290 },
2291 },
2292 'handler_configs': {
2293 'hand1' : {
2294 'class' : 'logging.StreamHandler',
2295 'formatter' : 'form1',
2296 'level' : 'NOTSET',
2297 'stream' : 'ext://sys.stdout',
2298 },
2299 },
2300 'formatters' : 'cfg://true_formatters',
2301 'handlers' : {
2302 'hand1' : 'cfg://handler_configs[hand1]',
2303 },
2304 'loggers' : {
2305 'compiler.parser' : {
2306 'level' : 'DEBUG',
2307 'handlers' : ['hand1'],
2308 },
2309 },
2310 'root' : {
2311 'level' : 'WARNING',
2312 },
2313 }
2314
2315 def apply_config(self, conf):
2316 logging.config.dictConfig(conf)
2317
2318 def test_config0_ok(self):
2319 # A simple config which overrides the default settings.
2320 with captured_stdout() as output:
2321 self.apply_config(self.config0)
2322 logger = logging.getLogger()
2323 # Won't output anything
2324 logger.info(self.next_message())
2325 # Outputs a message
2326 logger.error(self.next_message())
2327 self.assert_log_lines([
2328 ('ERROR', '2'),
2329 ], stream=output)
2330 # Original logger output is empty.
2331 self.assert_log_lines([])
2332
2333 def test_config1_ok(self, config=config1):
2334 # A config defining a sub-parser as well.
2335 with captured_stdout() as output:
2336 self.apply_config(config)
2337 logger = logging.getLogger("compiler.parser")
2338 # Both will output a message
2339 logger.info(self.next_message())
2340 logger.error(self.next_message())
2341 self.assert_log_lines([
2342 ('INFO', '1'),
2343 ('ERROR', '2'),
2344 ], stream=output)
2345 # Original logger output is empty.
2346 self.assert_log_lines([])
2347
2348 def test_config2_failure(self):
2349 # A simple config which overrides the default settings.
2350 self.assertRaises(Exception, self.apply_config, self.config2)
2351
2352 def test_config2a_failure(self):
2353 # A simple config which overrides the default settings.
2354 self.assertRaises(Exception, self.apply_config, self.config2a)
2355
2356 def test_config2b_failure(self):
2357 # A simple config which overrides the default settings.
2358 self.assertRaises(Exception, self.apply_config, self.config2b)
2359
2360 def test_config3_failure(self):
2361 # A simple config which overrides the default settings.
2362 self.assertRaises(Exception, self.apply_config, self.config3)
2363
2364 def test_config4_ok(self):
2365 # A config specifying a custom formatter class.
2366 with captured_stdout() as output:
2367 self.apply_config(self.config4)
2368 #logger = logging.getLogger()
2369 try:
2370 raise RuntimeError()
2371 except RuntimeError:
2372 logging.exception("just testing")
2373 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002374 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002375 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2376 # Original logger output is empty
2377 self.assert_log_lines([])
2378
2379 def test_config4a_ok(self):
2380 # A config specifying a custom formatter class.
2381 with captured_stdout() as output:
2382 self.apply_config(self.config4a)
2383 #logger = logging.getLogger()
2384 try:
2385 raise RuntimeError()
2386 except RuntimeError:
2387 logging.exception("just testing")
2388 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002389 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002390 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2391 # Original logger output is empty
2392 self.assert_log_lines([])
2393
2394 def test_config5_ok(self):
2395 self.test_config1_ok(config=self.config5)
2396
2397 def test_config6_failure(self):
2398 self.assertRaises(Exception, self.apply_config, self.config6)
2399
2400 def test_config7_ok(self):
2401 with captured_stdout() as output:
2402 self.apply_config(self.config1)
2403 logger = logging.getLogger("compiler.parser")
2404 # Both will output a message
2405 logger.info(self.next_message())
2406 logger.error(self.next_message())
2407 self.assert_log_lines([
2408 ('INFO', '1'),
2409 ('ERROR', '2'),
2410 ], stream=output)
2411 # Original logger output is empty.
2412 self.assert_log_lines([])
2413 with captured_stdout() as output:
2414 self.apply_config(self.config7)
2415 logger = logging.getLogger("compiler.parser")
2416 self.assertTrue(logger.disabled)
2417 logger = logging.getLogger("compiler.lexer")
2418 # Both will output a message
2419 logger.info(self.next_message())
2420 logger.error(self.next_message())
2421 self.assert_log_lines([
2422 ('INFO', '3'),
2423 ('ERROR', '4'),
2424 ], stream=output)
2425 # Original logger output is empty.
2426 self.assert_log_lines([])
2427
2428 #Same as test_config_7_ok but don't disable old loggers.
2429 def test_config_8_ok(self):
2430 with captured_stdout() as output:
2431 self.apply_config(self.config1)
2432 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002433 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002434 logger.info(self.next_message())
2435 logger.error(self.next_message())
2436 self.assert_log_lines([
2437 ('INFO', '1'),
2438 ('ERROR', '2'),
2439 ], stream=output)
2440 # Original logger output is empty.
2441 self.assert_log_lines([])
2442 with captured_stdout() as output:
2443 self.apply_config(self.config8)
2444 logger = logging.getLogger("compiler.parser")
2445 self.assertFalse(logger.disabled)
2446 # Both will output a message
2447 logger.info(self.next_message())
2448 logger.error(self.next_message())
2449 logger = logging.getLogger("compiler.lexer")
2450 # Both will output a message
2451 logger.info(self.next_message())
2452 logger.error(self.next_message())
2453 self.assert_log_lines([
2454 ('INFO', '3'),
2455 ('ERROR', '4'),
2456 ('INFO', '5'),
2457 ('ERROR', '6'),
2458 ], stream=output)
2459 # Original logger output is empty.
2460 self.assert_log_lines([])
2461
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002462 def test_config_8a_ok(self):
2463 with captured_stdout() as output:
2464 self.apply_config(self.config1a)
2465 logger = logging.getLogger("compiler.parser")
2466 # See issue #11424. compiler-hyphenated sorts
2467 # between compiler and compiler.xyz and this
2468 # was preventing compiler.xyz from being included
2469 # in the child loggers of compiler because of an
2470 # overzealous loop termination condition.
2471 hyphenated = logging.getLogger('compiler-hyphenated')
2472 # All will output a message
2473 logger.info(self.next_message())
2474 logger.error(self.next_message())
2475 hyphenated.critical(self.next_message())
2476 self.assert_log_lines([
2477 ('INFO', '1'),
2478 ('ERROR', '2'),
2479 ('CRITICAL', '3'),
2480 ], stream=output)
2481 # Original logger output is empty.
2482 self.assert_log_lines([])
2483 with captured_stdout() as output:
2484 self.apply_config(self.config8a)
2485 logger = logging.getLogger("compiler.parser")
2486 self.assertFalse(logger.disabled)
2487 # Both will output a message
2488 logger.info(self.next_message())
2489 logger.error(self.next_message())
2490 logger = logging.getLogger("compiler.lexer")
2491 # Both will output a message
2492 logger.info(self.next_message())
2493 logger.error(self.next_message())
2494 # Will not appear
2495 hyphenated.critical(self.next_message())
2496 self.assert_log_lines([
2497 ('INFO', '4'),
2498 ('ERROR', '5'),
2499 ('INFO', '6'),
2500 ('ERROR', '7'),
2501 ], stream=output)
2502 # Original logger output is empty.
2503 self.assert_log_lines([])
2504
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002505 def test_config_9_ok(self):
2506 with captured_stdout() as output:
2507 self.apply_config(self.config9)
2508 logger = logging.getLogger("compiler.parser")
2509 #Nothing will be output since both handler and logger are set to WARNING
2510 logger.info(self.next_message())
2511 self.assert_log_lines([], stream=output)
2512 self.apply_config(self.config9a)
2513 #Nothing will be output since both handler is still set to WARNING
2514 logger.info(self.next_message())
2515 self.assert_log_lines([], stream=output)
2516 self.apply_config(self.config9b)
2517 #Message should now be output
2518 logger.info(self.next_message())
2519 self.assert_log_lines([
2520 ('INFO', '3'),
2521 ], stream=output)
2522
2523 def test_config_10_ok(self):
2524 with captured_stdout() as output:
2525 self.apply_config(self.config10)
2526 logger = logging.getLogger("compiler.parser")
2527 logger.warning(self.next_message())
2528 logger = logging.getLogger('compiler')
2529 #Not output, because filtered
2530 logger.warning(self.next_message())
2531 logger = logging.getLogger('compiler.lexer')
2532 #Not output, because filtered
2533 logger.warning(self.next_message())
2534 logger = logging.getLogger("compiler.parser.codegen")
2535 #Output, as not filtered
2536 logger.error(self.next_message())
2537 self.assert_log_lines([
2538 ('WARNING', '1'),
2539 ('ERROR', '4'),
2540 ], stream=output)
2541
2542 def test_config11_ok(self):
2543 self.test_config1_ok(self.config11)
2544
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002545 def test_config12_failure(self):
2546 self.assertRaises(Exception, self.apply_config, self.config12)
2547
2548 def test_config13_failure(self):
2549 self.assertRaises(Exception, self.apply_config, self.config13)
2550
Victor Stinner45df8202010-04-28 22:31:17 +00002551 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002552 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002553 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002554 # Ask for a randomly assigned port (by using port 0)
2555 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002556 t.start()
2557 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002558 # Now get the port allocated
2559 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002560 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002561 try:
2562 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2563 sock.settimeout(2.0)
2564 sock.connect(('localhost', port))
2565
2566 slen = struct.pack('>L', len(text))
2567 s = slen + text
2568 sentsofar = 0
2569 left = len(s)
2570 while left > 0:
2571 sent = sock.send(s[sentsofar:])
2572 sentsofar += sent
2573 left -= sent
2574 sock.close()
2575 finally:
2576 t.ready.wait(2.0)
2577 logging.config.stopListening()
2578 t.join(2.0)
2579
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002580 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002581 def test_listen_config_10_ok(self):
2582 with captured_stdout() as output:
2583 self.setup_via_listener(json.dumps(self.config10))
2584 logger = logging.getLogger("compiler.parser")
2585 logger.warning(self.next_message())
2586 logger = logging.getLogger('compiler')
2587 #Not output, because filtered
2588 logger.warning(self.next_message())
2589 logger = logging.getLogger('compiler.lexer')
2590 #Not output, because filtered
2591 logger.warning(self.next_message())
2592 logger = logging.getLogger("compiler.parser.codegen")
2593 #Output, as not filtered
2594 logger.error(self.next_message())
2595 self.assert_log_lines([
2596 ('WARNING', '1'),
2597 ('ERROR', '4'),
2598 ], stream=output)
2599
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002600 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002601 def test_listen_config_1_ok(self):
2602 with captured_stdout() as output:
2603 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2604 logger = logging.getLogger("compiler.parser")
2605 # Both will output a message
2606 logger.info(self.next_message())
2607 logger.error(self.next_message())
2608 self.assert_log_lines([
2609 ('INFO', '1'),
2610 ('ERROR', '2'),
2611 ], stream=output)
2612 # Original logger output is empty.
2613 self.assert_log_lines([])
2614
Vinay Sajip373baef2011-04-26 20:05:24 +01002615 def test_baseconfig(self):
2616 d = {
2617 'atuple': (1, 2, 3),
2618 'alist': ['a', 'b', 'c'],
2619 'adict': {'d': 'e', 'f': 3 },
2620 'nest1': ('g', ('h', 'i'), 'j'),
2621 'nest2': ['k', ['l', 'm'], 'n'],
2622 'nest3': ['o', 'cfg://alist', 'p'],
2623 }
2624 bc = logging.config.BaseConfigurator(d)
2625 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2626 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2627 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2628 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2629 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2630 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2631 v = bc.convert('cfg://nest3')
2632 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2633 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2634 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2635 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002636
2637class ManagerTest(BaseTest):
2638 def test_manager_loggerclass(self):
2639 logged = []
2640
2641 class MyLogger(logging.Logger):
2642 def _log(self, level, msg, args, exc_info=None, extra=None):
2643 logged.append(msg)
2644
2645 man = logging.Manager(None)
2646 self.assertRaises(TypeError, man.setLoggerClass, int)
2647 man.setLoggerClass(MyLogger)
2648 logger = man.getLogger('test')
2649 logger.warning('should appear in logged')
2650 logging.warning('should not appear in logged')
2651
2652 self.assertEqual(logged, ['should appear in logged'])
2653
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002654 def test_set_log_record_factory(self):
2655 man = logging.Manager(None)
2656 expected = object()
2657 man.setLogRecordFactory(expected)
2658 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002659
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002660class ChildLoggerTest(BaseTest):
2661 def test_child_loggers(self):
2662 r = logging.getLogger()
2663 l1 = logging.getLogger('abc')
2664 l2 = logging.getLogger('def.ghi')
2665 c1 = r.getChild('xyz')
2666 c2 = r.getChild('uvw.xyz')
2667 self.assertTrue(c1 is logging.getLogger('xyz'))
2668 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2669 c1 = l1.getChild('def')
2670 c2 = c1.getChild('ghi')
2671 c3 = l1.getChild('def.ghi')
2672 self.assertTrue(c1 is logging.getLogger('abc.def'))
2673 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2674 self.assertTrue(c2 is c3)
2675
2676
Vinay Sajip6fac8172010-10-19 20:44:14 +00002677class DerivedLogRecord(logging.LogRecord):
2678 pass
2679
Vinay Sajip61561522010-12-03 11:50:38 +00002680class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002681
2682 def setUp(self):
2683 class CheckingFilter(logging.Filter):
2684 def __init__(self, cls):
2685 self.cls = cls
2686
2687 def filter(self, record):
2688 t = type(record)
2689 if t is not self.cls:
2690 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2691 self.cls)
2692 raise TypeError(msg)
2693 return True
2694
2695 BaseTest.setUp(self)
2696 self.filter = CheckingFilter(DerivedLogRecord)
2697 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002698 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002699
2700 def tearDown(self):
2701 self.root_logger.removeFilter(self.filter)
2702 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002703 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002704
2705 def test_logrecord_class(self):
2706 self.assertRaises(TypeError, self.root_logger.warning,
2707 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002708 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002709 self.root_logger.error(self.next_message())
2710 self.assert_log_lines([
2711 ('root', 'ERROR', '2'),
2712 ])
2713
2714
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002715class QueueHandlerTest(BaseTest):
2716 # Do not bother with a logger name group.
2717 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2718
2719 def setUp(self):
2720 BaseTest.setUp(self)
2721 self.queue = queue.Queue(-1)
2722 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2723 self.que_logger = logging.getLogger('que')
2724 self.que_logger.propagate = False
2725 self.que_logger.setLevel(logging.WARNING)
2726 self.que_logger.addHandler(self.que_hdlr)
2727
2728 def tearDown(self):
2729 self.que_hdlr.close()
2730 BaseTest.tearDown(self)
2731
2732 def test_queue_handler(self):
2733 self.que_logger.debug(self.next_message())
2734 self.assertRaises(queue.Empty, self.queue.get_nowait)
2735 self.que_logger.info(self.next_message())
2736 self.assertRaises(queue.Empty, self.queue.get_nowait)
2737 msg = self.next_message()
2738 self.que_logger.warning(msg)
2739 data = self.queue.get_nowait()
2740 self.assertTrue(isinstance(data, logging.LogRecord))
2741 self.assertEqual(data.name, self.que_logger.name)
2742 self.assertEqual((data.msg, data.args), (msg, None))
2743
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002744 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2745 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002746 def test_queue_listener(self):
2747 handler = TestHandler(Matcher())
2748 listener = logging.handlers.QueueListener(self.queue, handler)
2749 listener.start()
2750 try:
2751 self.que_logger.warning(self.next_message())
2752 self.que_logger.error(self.next_message())
2753 self.que_logger.critical(self.next_message())
2754 finally:
2755 listener.stop()
2756 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2757 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2758 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2759
Vinay Sajip37eb3382011-04-26 20:26:41 +01002760ZERO = datetime.timedelta(0)
2761
2762class UTC(datetime.tzinfo):
2763 def utcoffset(self, dt):
2764 return ZERO
2765
2766 dst = utcoffset
2767
2768 def tzname(self, dt):
2769 return 'UTC'
2770
2771utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002772
Vinay Sajipa39c5712010-10-25 13:57:39 +00002773class FormatterTest(unittest.TestCase):
2774 def setUp(self):
2775 self.common = {
2776 'name': 'formatter.test',
2777 'level': logging.DEBUG,
2778 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2779 'lineno': 42,
2780 'exc_info': None,
2781 'func': None,
2782 'msg': 'Message with %d %s',
2783 'args': (2, 'placeholders'),
2784 }
2785 self.variants = {
2786 }
2787
2788 def get_record(self, name=None):
2789 result = dict(self.common)
2790 if name is not None:
2791 result.update(self.variants[name])
2792 return logging.makeLogRecord(result)
2793
2794 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002795 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002796 r = self.get_record()
2797 f = logging.Formatter('${%(message)s}')
2798 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2799 f = logging.Formatter('%(random)s')
2800 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002801 self.assertFalse(f.usesTime())
2802 f = logging.Formatter('%(asctime)s')
2803 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002804 f = logging.Formatter('%(asctime)-15s')
2805 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002806 f = logging.Formatter('asctime')
2807 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002808
2809 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002810 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002811 r = self.get_record()
2812 f = logging.Formatter('$%{message}%$', style='{')
2813 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2814 f = logging.Formatter('{random}', style='{')
2815 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002816 self.assertFalse(f.usesTime())
2817 f = logging.Formatter('{asctime}', style='{')
2818 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002819 f = logging.Formatter('{asctime!s:15}', style='{')
2820 self.assertTrue(f.usesTime())
2821 f = logging.Formatter('{asctime:15}', style='{')
2822 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002823 f = logging.Formatter('asctime', style='{')
2824 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002825
2826 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002827 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002828 r = self.get_record()
2829 f = logging.Formatter('$message', style='$')
2830 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2831 f = logging.Formatter('$$%${message}%$$', style='$')
2832 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2833 f = logging.Formatter('${random}', style='$')
2834 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002835 self.assertFalse(f.usesTime())
2836 f = logging.Formatter('${asctime}', style='$')
2837 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002838 f = logging.Formatter('${asctime', style='$')
2839 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002840 f = logging.Formatter('$asctime', style='$')
2841 self.assertTrue(f.usesTime())
2842 f = logging.Formatter('asctime', style='$')
2843 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002844
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002845 def test_invalid_style(self):
2846 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2847
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002848 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002849 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002850 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002851 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002852 r.msecs = 123
2853 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002854 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002855 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2856 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002857 f.format(r)
2858 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2859
2860class TestBufferingFormatter(logging.BufferingFormatter):
2861 def formatHeader(self, records):
2862 return '[(%d)' % len(records)
2863
2864 def formatFooter(self, records):
2865 return '(%d)]' % len(records)
2866
2867class BufferingFormatterTest(unittest.TestCase):
2868 def setUp(self):
2869 self.records = [
2870 logging.makeLogRecord({'msg': 'one'}),
2871 logging.makeLogRecord({'msg': 'two'}),
2872 ]
2873
2874 def test_default(self):
2875 f = logging.BufferingFormatter()
2876 self.assertEqual('', f.format([]))
2877 self.assertEqual('onetwo', f.format(self.records))
2878
2879 def test_custom(self):
2880 f = TestBufferingFormatter()
2881 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2882 lf = logging.Formatter('<%(message)s>')
2883 f = TestBufferingFormatter(lf)
2884 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002885
2886class ExceptionTest(BaseTest):
2887 def test_formatting(self):
2888 r = self.root_logger
2889 h = RecordingHandler()
2890 r.addHandler(h)
2891 try:
2892 raise RuntimeError('deliberate mistake')
2893 except:
2894 logging.exception('failed', stack_info=True)
2895 r.removeHandler(h)
2896 h.close()
2897 r = h.records[0]
2898 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2899 'call last):\n'))
2900 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2901 'deliberate mistake'))
2902 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2903 'call last):\n'))
2904 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2905 'stack_info=True)'))
2906
2907
Vinay Sajip5a27d402010-12-10 11:42:57 +00002908class LastResortTest(BaseTest):
2909 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002910 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002911 root = self.root_logger
2912 root.removeHandler(self.root_hdlr)
2913 old_stderr = sys.stderr
2914 old_lastresort = logging.lastResort
2915 old_raise_exceptions = logging.raiseExceptions
2916 try:
2917 sys.stderr = sio = io.StringIO()
2918 root.warning('This is your final chance!')
2919 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2920 #No handlers and no last resort, so 'No handlers' message
2921 logging.lastResort = None
2922 sys.stderr = sio = io.StringIO()
2923 root.warning('This is your final chance!')
2924 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2925 # 'No handlers' message only printed once
2926 sys.stderr = sio = io.StringIO()
2927 root.warning('This is your final chance!')
2928 self.assertEqual(sio.getvalue(), '')
2929 root.manager.emittedNoHandlerWarning = False
2930 #If raiseExceptions is False, no message is printed
2931 logging.raiseExceptions = False
2932 sys.stderr = sio = io.StringIO()
2933 root.warning('This is your final chance!')
2934 self.assertEqual(sio.getvalue(), '')
2935 finally:
2936 sys.stderr = old_stderr
2937 root.addHandler(self.root_hdlr)
2938 logging.lastResort = old_lastresort
2939 logging.raiseExceptions = old_raise_exceptions
2940
2941
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002942class FakeHandler:
2943
2944 def __init__(self, identifier, called):
2945 for method in ('acquire', 'flush', 'close', 'release'):
2946 setattr(self, method, self.record_call(identifier, method, called))
2947
2948 def record_call(self, identifier, method_name, called):
2949 def inner():
2950 called.append('{} - {}'.format(identifier, method_name))
2951 return inner
2952
2953
2954class RecordingHandler(logging.NullHandler):
2955
2956 def __init__(self, *args, **kwargs):
2957 super(RecordingHandler, self).__init__(*args, **kwargs)
2958 self.records = []
2959
2960 def handle(self, record):
2961 """Keep track of all the emitted records."""
2962 self.records.append(record)
2963
2964
2965class ShutdownTest(BaseTest):
2966
Vinay Sajip5e66b162011-04-20 15:41:14 +01002967 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002968
2969 def setUp(self):
2970 super(ShutdownTest, self).setUp()
2971 self.called = []
2972
2973 raise_exceptions = logging.raiseExceptions
2974 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2975
2976 def raise_error(self, error):
2977 def inner():
2978 raise error()
2979 return inner
2980
2981 def test_no_failure(self):
2982 # create some fake handlers
2983 handler0 = FakeHandler(0, self.called)
2984 handler1 = FakeHandler(1, self.called)
2985 handler2 = FakeHandler(2, self.called)
2986
2987 # create live weakref to those handlers
2988 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2989
2990 logging.shutdown(handlerList=list(handlers))
2991
2992 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2993 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2994 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2995 self.assertEqual(expected, self.called)
2996
2997 def _test_with_failure_in_method(self, method, error):
2998 handler = FakeHandler(0, self.called)
2999 setattr(handler, method, self.raise_error(error))
3000 handlers = [logging.weakref.ref(handler)]
3001
3002 logging.shutdown(handlerList=list(handlers))
3003
3004 self.assertEqual('0 - release', self.called[-1])
3005
3006 def test_with_ioerror_in_acquire(self):
3007 self._test_with_failure_in_method('acquire', IOError)
3008
3009 def test_with_ioerror_in_flush(self):
3010 self._test_with_failure_in_method('flush', IOError)
3011
3012 def test_with_ioerror_in_close(self):
3013 self._test_with_failure_in_method('close', IOError)
3014
3015 def test_with_valueerror_in_acquire(self):
3016 self._test_with_failure_in_method('acquire', ValueError)
3017
3018 def test_with_valueerror_in_flush(self):
3019 self._test_with_failure_in_method('flush', ValueError)
3020
3021 def test_with_valueerror_in_close(self):
3022 self._test_with_failure_in_method('close', ValueError)
3023
3024 def test_with_other_error_in_acquire_without_raise(self):
3025 logging.raiseExceptions = False
3026 self._test_with_failure_in_method('acquire', IndexError)
3027
3028 def test_with_other_error_in_flush_without_raise(self):
3029 logging.raiseExceptions = False
3030 self._test_with_failure_in_method('flush', IndexError)
3031
3032 def test_with_other_error_in_close_without_raise(self):
3033 logging.raiseExceptions = False
3034 self._test_with_failure_in_method('close', IndexError)
3035
3036 def test_with_other_error_in_acquire_with_raise(self):
3037 logging.raiseExceptions = True
3038 self.assertRaises(IndexError, self._test_with_failure_in_method,
3039 'acquire', IndexError)
3040
3041 def test_with_other_error_in_flush_with_raise(self):
3042 logging.raiseExceptions = True
3043 self.assertRaises(IndexError, self._test_with_failure_in_method,
3044 'flush', IndexError)
3045
3046 def test_with_other_error_in_close_with_raise(self):
3047 logging.raiseExceptions = True
3048 self.assertRaises(IndexError, self._test_with_failure_in_method,
3049 'close', IndexError)
3050
3051
3052class ModuleLevelMiscTest(BaseTest):
3053
Vinay Sajip5e66b162011-04-20 15:41:14 +01003054 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003055
3056 def test_disable(self):
3057 old_disable = logging.root.manager.disable
3058 # confirm our assumptions are correct
3059 assert old_disable == 0
3060 self.addCleanup(lambda: logging.disable(old_disable))
3061
3062 logging.disable(83)
3063 self.assertEqual(logging.root.manager.disable, 83)
3064
3065 def _test_log(self, method, level=None):
3066 called = []
3067 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003068 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003069
3070 recording = RecordingHandler()
3071 logging.root.addHandler(recording)
3072
3073 log_method = getattr(logging, method)
3074 if level is not None:
3075 log_method(level, "test me: %r", recording)
3076 else:
3077 log_method("test me: %r", recording)
3078
3079 self.assertEqual(len(recording.records), 1)
3080 record = recording.records[0]
3081 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3082
3083 expected_level = level if level is not None else getattr(logging, method.upper())
3084 self.assertEqual(record.levelno, expected_level)
3085
3086 # basicConfig was not called!
3087 self.assertEqual(called, [])
3088
3089 def test_log(self):
3090 self._test_log('log', logging.ERROR)
3091
3092 def test_debug(self):
3093 self._test_log('debug')
3094
3095 def test_info(self):
3096 self._test_log('info')
3097
3098 def test_warning(self):
3099 self._test_log('warning')
3100
3101 def test_error(self):
3102 self._test_log('error')
3103
3104 def test_critical(self):
3105 self._test_log('critical')
3106
3107 def test_set_logger_class(self):
3108 self.assertRaises(TypeError, logging.setLoggerClass, object)
3109
3110 class MyLogger(logging.Logger):
3111 pass
3112
3113 logging.setLoggerClass(MyLogger)
3114 self.assertEqual(logging.getLoggerClass(), MyLogger)
3115
3116 logging.setLoggerClass(logging.Logger)
3117 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3118
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003119class LogRecordTest(BaseTest):
3120 def test_str_rep(self):
3121 r = logging.makeLogRecord({})
3122 s = str(r)
3123 self.assertTrue(s.startswith('<LogRecord: '))
3124 self.assertTrue(s.endswith('>'))
3125
3126 def test_dict_arg(self):
3127 h = RecordingHandler()
3128 r = logging.getLogger()
3129 r.addHandler(h)
3130 d = {'less' : 'more' }
3131 logging.warning('less is %(less)s', d)
3132 self.assertIs(h.records[0].args, d)
3133 self.assertEqual(h.records[0].message, 'less is more')
3134 r.removeHandler(h)
3135 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003136
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003137 def test_multiprocessing(self):
3138 r = logging.makeLogRecord({})
3139 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003140 try:
3141 import multiprocessing as mp
3142 r = logging.makeLogRecord({})
3143 self.assertEqual(r.processName, mp.current_process().name)
3144 except ImportError:
3145 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003146
3147 def test_optional(self):
3148 r = logging.makeLogRecord({})
3149 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003150 if threading:
3151 NOT_NONE(r.thread)
3152 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003153 NOT_NONE(r.process)
3154 NOT_NONE(r.processName)
3155 log_threads = logging.logThreads
3156 log_processes = logging.logProcesses
3157 log_multiprocessing = logging.logMultiprocessing
3158 try:
3159 logging.logThreads = False
3160 logging.logProcesses = False
3161 logging.logMultiprocessing = False
3162 r = logging.makeLogRecord({})
3163 NONE = self.assertIsNone
3164 NONE(r.thread)
3165 NONE(r.threadName)
3166 NONE(r.process)
3167 NONE(r.processName)
3168 finally:
3169 logging.logThreads = log_threads
3170 logging.logProcesses = log_processes
3171 logging.logMultiprocessing = log_multiprocessing
3172
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003173class BasicConfigTest(unittest.TestCase):
3174
Vinay Sajip95bf5042011-04-20 11:50:56 +01003175 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003176
3177 def setUp(self):
3178 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003179 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003180 self.saved_handlers = logging._handlers.copy()
3181 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003182 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003183 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003184 logging.root.handlers = []
3185
3186 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003187 for h in logging.root.handlers[:]:
3188 logging.root.removeHandler(h)
3189 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003190 super(BasicConfigTest, self).tearDown()
3191
Vinay Sajip3def7e02011-04-20 10:58:06 +01003192 def cleanup(self):
3193 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003194 logging._handlers.clear()
3195 logging._handlers.update(self.saved_handlers)
3196 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003197 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003198
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003199 def test_no_kwargs(self):
3200 logging.basicConfig()
3201
3202 # handler defaults to a StreamHandler to sys.stderr
3203 self.assertEqual(len(logging.root.handlers), 1)
3204 handler = logging.root.handlers[0]
3205 self.assertIsInstance(handler, logging.StreamHandler)
3206 self.assertEqual(handler.stream, sys.stderr)
3207
3208 formatter = handler.formatter
3209 # format defaults to logging.BASIC_FORMAT
3210 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3211 # datefmt defaults to None
3212 self.assertIsNone(formatter.datefmt)
3213 # style defaults to %
3214 self.assertIsInstance(formatter._style, logging.PercentStyle)
3215
3216 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003217 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003218
3219 def test_filename(self):
3220 logging.basicConfig(filename='test.log')
3221
3222 self.assertEqual(len(logging.root.handlers), 1)
3223 handler = logging.root.handlers[0]
3224 self.assertIsInstance(handler, logging.FileHandler)
3225
3226 expected = logging.FileHandler('test.log', 'a')
3227 self.addCleanup(expected.close)
3228 self.assertEqual(handler.stream.mode, expected.stream.mode)
3229 self.assertEqual(handler.stream.name, expected.stream.name)
3230
3231 def test_filemode(self):
3232 logging.basicConfig(filename='test.log', filemode='wb')
3233
3234 handler = logging.root.handlers[0]
3235 expected = logging.FileHandler('test.log', 'wb')
3236 self.addCleanup(expected.close)
3237 self.assertEqual(handler.stream.mode, expected.stream.mode)
3238
3239 def test_stream(self):
3240 stream = io.StringIO()
3241 self.addCleanup(stream.close)
3242 logging.basicConfig(stream=stream)
3243
3244 self.assertEqual(len(logging.root.handlers), 1)
3245 handler = logging.root.handlers[0]
3246 self.assertIsInstance(handler, logging.StreamHandler)
3247 self.assertEqual(handler.stream, stream)
3248
3249 def test_format(self):
3250 logging.basicConfig(format='foo')
3251
3252 formatter = logging.root.handlers[0].formatter
3253 self.assertEqual(formatter._style._fmt, 'foo')
3254
3255 def test_datefmt(self):
3256 logging.basicConfig(datefmt='bar')
3257
3258 formatter = logging.root.handlers[0].formatter
3259 self.assertEqual(formatter.datefmt, 'bar')
3260
3261 def test_style(self):
3262 logging.basicConfig(style='$')
3263
3264 formatter = logging.root.handlers[0].formatter
3265 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3266
3267 def test_level(self):
3268 old_level = logging.root.level
3269 self.addCleanup(lambda: logging.root.setLevel(old_level))
3270
3271 logging.basicConfig(level=57)
3272 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003273 # Test that second call has no effect
3274 logging.basicConfig(level=58)
3275 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003276
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003277 def test_incompatible(self):
3278 assertRaises = self.assertRaises
3279 handlers = [logging.StreamHandler()]
3280 stream = sys.stderr
3281 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3282 stream=stream)
3283 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3284 handlers=handlers)
3285 assertRaises(ValueError, logging.basicConfig, stream=stream,
3286 handlers=handlers)
3287
3288 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003289 handlers = [
3290 logging.StreamHandler(),
3291 logging.StreamHandler(sys.stdout),
3292 logging.StreamHandler(),
3293 ]
3294 f = logging.Formatter()
3295 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003296 logging.basicConfig(handlers=handlers)
3297 self.assertIs(handlers[0], logging.root.handlers[0])
3298 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003299 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003300 self.assertIsNotNone(handlers[0].formatter)
3301 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003302 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003303 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3304
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003305 def _test_log(self, method, level=None):
3306 # logging.root has no handlers so basicConfig should be called
3307 called = []
3308
3309 old_basic_config = logging.basicConfig
3310 def my_basic_config(*a, **kw):
3311 old_basic_config()
3312 old_level = logging.root.level
3313 logging.root.setLevel(100) # avoid having messages in stderr
3314 self.addCleanup(lambda: logging.root.setLevel(old_level))
3315 called.append((a, kw))
3316
3317 patch(self, logging, 'basicConfig', my_basic_config)
3318
3319 log_method = getattr(logging, method)
3320 if level is not None:
3321 log_method(level, "test me")
3322 else:
3323 log_method("test me")
3324
3325 # basicConfig was called with no arguments
3326 self.assertEqual(called, [((), {})])
3327
3328 def test_log(self):
3329 self._test_log('log', logging.WARNING)
3330
3331 def test_debug(self):
3332 self._test_log('debug')
3333
3334 def test_info(self):
3335 self._test_log('info')
3336
3337 def test_warning(self):
3338 self._test_log('warning')
3339
3340 def test_error(self):
3341 self._test_log('error')
3342
3343 def test_critical(self):
3344 self._test_log('critical')
3345
3346
3347class LoggerAdapterTest(unittest.TestCase):
3348
3349 def setUp(self):
3350 super(LoggerAdapterTest, self).setUp()
3351 old_handler_list = logging._handlerList[:]
3352
3353 self.recording = RecordingHandler()
3354 self.logger = logging.root
3355 self.logger.addHandler(self.recording)
3356 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3357 self.addCleanup(self.recording.close)
3358
3359 def cleanup():
3360 logging._handlerList[:] = old_handler_list
3361
3362 self.addCleanup(cleanup)
3363 self.addCleanup(logging.shutdown)
3364 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3365
3366 def test_exception(self):
3367 msg = 'testing exception: %r'
3368 exc = None
3369 try:
3370 assert False
3371 except AssertionError as e:
3372 exc = e
3373 self.adapter.exception(msg, self.recording)
3374
3375 self.assertEqual(len(self.recording.records), 1)
3376 record = self.recording.records[0]
3377 self.assertEqual(record.levelno, logging.ERROR)
3378 self.assertEqual(record.msg, msg)
3379 self.assertEqual(record.args, (self.recording,))
3380 self.assertEqual(record.exc_info,
3381 (exc.__class__, exc, exc.__traceback__))
3382
3383 def test_critical(self):
3384 msg = 'critical test! %r'
3385 self.adapter.critical(msg, self.recording)
3386
3387 self.assertEqual(len(self.recording.records), 1)
3388 record = self.recording.records[0]
3389 self.assertEqual(record.levelno, logging.CRITICAL)
3390 self.assertEqual(record.msg, msg)
3391 self.assertEqual(record.args, (self.recording,))
3392
3393 def test_is_enabled_for(self):
3394 old_disable = self.adapter.logger.manager.disable
3395 self.adapter.logger.manager.disable = 33
3396 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3397 'disable', old_disable))
3398 self.assertFalse(self.adapter.isEnabledFor(32))
3399
3400 def test_has_handlers(self):
3401 self.assertTrue(self.adapter.hasHandlers())
3402
3403 for handler in self.logger.handlers:
3404 self.logger.removeHandler(handler)
3405 assert not self.logger.hasHandlers()
3406
3407 self.assertFalse(self.adapter.hasHandlers())
3408
3409
3410class LoggerTest(BaseTest):
3411
3412 def setUp(self):
3413 super(LoggerTest, self).setUp()
3414 self.recording = RecordingHandler()
3415 self.logger = logging.Logger(name='blah')
3416 self.logger.addHandler(self.recording)
3417 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3418 self.addCleanup(self.recording.close)
3419 self.addCleanup(logging.shutdown)
3420
3421 def test_set_invalid_level(self):
3422 self.assertRaises(TypeError, self.logger.setLevel, object())
3423
3424 def test_exception(self):
3425 msg = 'testing exception: %r'
3426 exc = None
3427 try:
3428 assert False
3429 except AssertionError as e:
3430 exc = e
3431 self.logger.exception(msg, self.recording)
3432
3433 self.assertEqual(len(self.recording.records), 1)
3434 record = self.recording.records[0]
3435 self.assertEqual(record.levelno, logging.ERROR)
3436 self.assertEqual(record.msg, msg)
3437 self.assertEqual(record.args, (self.recording,))
3438 self.assertEqual(record.exc_info,
3439 (exc.__class__, exc, exc.__traceback__))
3440
3441 def test_log_invalid_level_with_raise(self):
3442 old_raise = logging.raiseExceptions
3443 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3444
3445 logging.raiseExceptions = True
3446 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3447
3448 def test_log_invalid_level_no_raise(self):
3449 old_raise = logging.raiseExceptions
3450 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3451
3452 logging.raiseExceptions = False
3453 self.logger.log('10', 'test message') # no exception happens
3454
3455 def test_find_caller_with_stack_info(self):
3456 called = []
3457 patch(self, logging.traceback, 'print_stack',
3458 lambda f, file: called.append(file.getvalue()))
3459
3460 self.logger.findCaller(stack_info=True)
3461
3462 self.assertEqual(len(called), 1)
3463 self.assertEqual('Stack (most recent call last):\n', called[0])
3464
3465 def test_make_record_with_extra_overwrite(self):
3466 name = 'my record'
3467 level = 13
3468 fn = lno = msg = args = exc_info = func = sinfo = None
3469 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3470 exc_info, func, sinfo)
3471
3472 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3473 extra = {key: 'some value'}
3474 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3475 fn, lno, msg, args, exc_info,
3476 extra=extra, sinfo=sinfo)
3477
3478 def test_make_record_with_extra_no_overwrite(self):
3479 name = 'my record'
3480 level = 13
3481 fn = lno = msg = args = exc_info = func = sinfo = None
3482 extra = {'valid_key': 'some value'}
3483 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3484 exc_info, extra=extra, sinfo=sinfo)
3485 self.assertIn('valid_key', result.__dict__)
3486
3487 def test_has_handlers(self):
3488 self.assertTrue(self.logger.hasHandlers())
3489
3490 for handler in self.logger.handlers:
3491 self.logger.removeHandler(handler)
3492 assert not self.logger.hasHandlers()
3493
3494 self.assertFalse(self.logger.hasHandlers())
3495
3496 def test_has_handlers_no_propagate(self):
3497 child_logger = logging.getLogger('blah.child')
3498 child_logger.propagate = False
3499 assert child_logger.handlers == []
3500
3501 self.assertFalse(child_logger.hasHandlers())
3502
3503 def test_is_enabled_for(self):
3504 old_disable = self.logger.manager.disable
3505 self.logger.manager.disable = 23
3506 self.addCleanup(lambda: setattr(self.logger.manager,
3507 'disable', old_disable))
3508 self.assertFalse(self.logger.isEnabledFor(22))
3509
3510
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003511class BaseFileTest(BaseTest):
3512 "Base class for handler tests that write log files"
3513
3514 def setUp(self):
3515 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003516 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3517 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003518 self.rmfiles = []
3519
3520 def tearDown(self):
3521 for fn in self.rmfiles:
3522 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003523 if os.path.exists(self.fn):
3524 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003525 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003526
3527 def assertLogFile(self, filename):
3528 "Assert a log file is there and register it for deletion"
3529 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003530 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003531 self.rmfiles.append(filename)
3532
3533
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003534class FileHandlerTest(BaseFileTest):
3535 def test_delay(self):
3536 os.unlink(self.fn)
3537 fh = logging.FileHandler(self.fn, delay=True)
3538 self.assertIsNone(fh.stream)
3539 self.assertFalse(os.path.exists(self.fn))
3540 fh.handle(logging.makeLogRecord({}))
3541 self.assertIsNotNone(fh.stream)
3542 self.assertTrue(os.path.exists(self.fn))
3543 fh.close()
3544
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003545class RotatingFileHandlerTest(BaseFileTest):
3546 def next_rec(self):
3547 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3548 self.next_message(), None, None, None)
3549
3550 def test_should_not_rollover(self):
3551 # If maxbytes is zero rollover never occurs
3552 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3553 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003554 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003555
3556 def test_should_rollover(self):
3557 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3558 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003559 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003560
3561 def test_file_created(self):
3562 # checks that the file is created and assumes it was created
3563 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003564 rh = logging.handlers.RotatingFileHandler(self.fn)
3565 rh.emit(self.next_rec())
3566 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003567 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003568
3569 def test_rollover_filenames(self):
3570 rh = logging.handlers.RotatingFileHandler(
3571 self.fn, backupCount=2, maxBytes=1)
3572 rh.emit(self.next_rec())
3573 self.assertLogFile(self.fn)
3574 rh.emit(self.next_rec())
3575 self.assertLogFile(self.fn + ".1")
3576 rh.emit(self.next_rec())
3577 self.assertLogFile(self.fn + ".2")
3578 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003579 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003580
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003581class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003582 # other test methods added below
3583 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003584 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3585 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003586 r = logging.makeLogRecord({'msg': 'testing'})
3587 fh.emit(r)
3588 self.assertLogFile(self.fn)
3589 time.sleep(1.0)
3590 fh.emit(r)
3591 fh.close()
3592 # At this point, we should have a recent rotated file which we
3593 # can test for the existence of. However, in practice, on some
3594 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003595 # in time to go to look for the log file. So, we go back a fair
3596 # bit, and stop as soon as we see a rotated file. In theory this
3597 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003598 found = False
3599 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003600 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003601 for secs in range(GO_BACK):
3602 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003603 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3604 found = os.path.exists(fn)
3605 if found:
3606 self.rmfiles.append(fn)
3607 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003608 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3609 if not found:
3610 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003611 dn, fn = os.path.split(self.fn)
3612 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003613 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3614 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajipfd285022011-05-07 17:01:22 +01003615 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003616
Vinay Sajip0372e102011-05-05 12:59:14 +01003617 def test_invalid(self):
3618 assertRaises = self.assertRaises
3619 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003620 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003621 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003622 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003623 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003624 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003625
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003626def secs(**kw):
3627 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3628
3629for when, exp in (('S', 1),
3630 ('M', 60),
3631 ('H', 60 * 60),
3632 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003633 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003634 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003635 ('W0', secs(days=4, hours=24)),
3636 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003637 def test_compute_rollover(self, when=when, exp=exp):
3638 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003639 self.fn, when=when, interval=1, backupCount=0, utc=True)
3640 currentTime = 0.0
3641 actual = rh.computeRollover(currentTime)
3642 if exp != actual:
3643 # Failures occur on some systems for MIDNIGHT and W0.
3644 # Print detailed calculation for MIDNIGHT so we can try to see
3645 # what's going on
3646 import time
3647 if when == 'MIDNIGHT':
3648 try:
3649 if rh.utc:
3650 t = time.gmtime(currentTime)
3651 else:
3652 t = time.localtime(currentTime)
3653 currentHour = t[3]
3654 currentMinute = t[4]
3655 currentSecond = t[5]
3656 # r is the number of seconds left between now and midnight
3657 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3658 currentMinute) * 60 +
3659 currentSecond)
3660 result = currentTime + r
3661 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3662 print('currentHour: %s' % currentHour, file=sys.stderr)
3663 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3664 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3665 print('r: %s' % r, file=sys.stderr)
3666 print('result: %s' % result, file=sys.stderr)
3667 except Exception:
3668 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3669 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003670 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003671 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3672
Vinay Sajip60ccd822011-05-09 17:32:09 +01003673
3674@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3675class NTEventLogHandlerTest(BaseTest):
3676 def test_basic(self):
3677 logtype = 'Application'
3678 elh = win32evtlog.OpenEventLog(None, logtype)
3679 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3680 h = logging.handlers.NTEventLogHandler('test_logging')
3681 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3682 h.handle(r)
3683 h.close()
3684 # Now see if the event is recorded
3685 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3686 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3687 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3688 found = False
3689 GO_BACK = 100
3690 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3691 for e in events:
3692 if e.SourceName != 'test_logging':
3693 continue
3694 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3695 if msg != 'Test Log Message\r\n':
3696 continue
3697 found = True
3698 break
3699 msg = 'Record not found in event log, went back %d records' % GO_BACK
3700 self.assertTrue(found, msg=msg)
3701
Christian Heimes180510d2008-03-03 19:15:45 +00003702# Set the locale to the platform-dependent default. I have no idea
3703# why the test does this, but in any case we save the current locale
3704# first and restore it at the end.
3705@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003706def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003707 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003708 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003709 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3710 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3711 ManagerTest, FormatterTest, BufferingFormatterTest,
3712 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3713 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3714 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3715 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003716 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003717 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003718 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003719 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003720
Christian Heimes180510d2008-03-03 19:15:45 +00003721if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003722 test_main()