blob: 3118065a76c602f4e776c5214f44d6237384d065 [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajiped0473c2011-02-26 15:35:38 +00003# Copyright 2001-2011 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00004#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19"""Test harness for the logging module. Run all tests.
20
Vinay Sajiped0473c2011-02-26 15:35:38 +000021Copyright (C) 2001-2011 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Christian Heimes180510d2008-03-03 19:15:45 +000024import logging
25import logging.handlers
26import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000027
Benjamin Petersonf91df042009-02-13 02:50:59 +000028import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Christian Heimes180510d2008-03-03 19:15:45 +000030import pickle
31import io
32import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000033import json
Christian Heimes180510d2008-03-03 19:15:45 +000034import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000035import queue
Christian Heimes180510d2008-03-03 19:15:45 +000036import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000037import select
Christian Heimes180510d2008-03-03 19:15:45 +000038import socket
Christian Heimes180510d2008-03-03 19:15:45 +000039import struct
40import sys
41import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010042from test.support import captured_stdout, run_with_locale, run_unittest, patch
Vinay Sajipe723e962011-04-15 22:27:17 +010043from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000044import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010045import time
Christian Heimes180510d2008-03-03 19:15:45 +000046import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000047import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000048import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000049try:
50 import threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010051 # The following imports are needed only for tests which
52 import asynchat
53 import asyncore
54 import errno
55 from http.server import HTTPServer, BaseHTTPRequestHandler
56 import smtpd
57 from urllib.parse import urlparse, parse_qs
58 from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
59 ThreadingTCPServer, StreamRequestHandler)
Victor Stinner45df8202010-04-28 22:31:17 +000060except ImportError:
61 threading = None
Vinay Sajip60ccd822011-05-09 17:32:09 +010062try:
63 import win32evtlog
64except ImportError:
65 win32evtlog = None
66try:
67 import win32evtlogutil
68except ImportError:
69 win32evtlogutil = None
70 win32evtlog = None
Christian Heimes18c66892008-02-17 13:31:39 +000071
72
Christian Heimes180510d2008-03-03 19:15:45 +000073class BaseTest(unittest.TestCase):
74
75 """Base class for logging tests."""
76
77 log_format = "%(name)s -> %(levelname)s: %(message)s"
78 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
79 message_num = 0
80
81 def setUp(self):
82 """Setup the default logging stream to an internal StringIO instance,
83 so that we can examine log output as we want."""
84 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000085 logging._acquireLock()
86 try:
Christian Heimes180510d2008-03-03 19:15:45 +000087 self.saved_handlers = logging._handlers.copy()
88 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000089 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000090 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000091 self.logger_states = logger_states = {}
92 for name in saved_loggers:
93 logger_states[name] = getattr(saved_loggers[name],
94 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000095 finally:
96 logging._releaseLock()
97
Benjamin Peterson22005fc2010-04-11 16:25:06 +000098 # Set two unused loggers: one non-ASCII and one Unicode.
99 # This is to test correct operation when sorting existing
100 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +0000101 self.logger1 = logging.getLogger("\xab\xd7\xbb")
102 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000103
Christian Heimes180510d2008-03-03 19:15:45 +0000104 self.root_logger = logging.getLogger("")
105 self.original_logging_level = self.root_logger.getEffectiveLevel()
106
107 self.stream = io.StringIO()
108 self.root_logger.setLevel(logging.DEBUG)
109 self.root_hdlr = logging.StreamHandler(self.stream)
110 self.root_formatter = logging.Formatter(self.log_format)
111 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000112 if self.logger1.hasHandlers():
113 hlist = self.logger1.handlers + self.root_logger.handlers
114 raise AssertionError('Unexpected handlers: %s' % hlist)
115 if self.logger2.hasHandlers():
116 hlist = self.logger2.handlers + self.root_logger.handlers
117 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000118 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000119 self.assertTrue(self.logger1.hasHandlers())
120 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000121
122 def tearDown(self):
123 """Remove our logging stream, and restore the original logging
124 level."""
125 self.stream.close()
126 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000127 while self.root_logger.handlers:
128 h = self.root_logger.handlers[0]
129 self.root_logger.removeHandler(h)
130 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000131 self.root_logger.setLevel(self.original_logging_level)
132 logging._acquireLock()
133 try:
134 logging._levelNames.clear()
135 logging._levelNames.update(self.saved_level_names)
136 logging._handlers.clear()
137 logging._handlers.update(self.saved_handlers)
138 logging._handlerList[:] = self.saved_handler_list
139 loggerDict = logging.getLogger().manager.loggerDict
140 loggerDict.clear()
141 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000142 logger_states = self.logger_states
143 for name in self.logger_states:
144 if logger_states[name] is not None:
145 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000146 finally:
147 logging._releaseLock()
148
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000149 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000150 """Match the collected log lines against the regular expression
151 self.expected_log_pat, and compare the extracted group values to
152 the expected_values list of tuples."""
153 stream = stream or self.stream
154 pat = re.compile(self.expected_log_pat)
155 try:
156 stream.reset()
157 actual_lines = stream.readlines()
158 except AttributeError:
159 # StringIO.StringIO lacks a reset() method.
160 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000161 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000162 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000163 for actual, expected in zip(actual_lines, expected_values):
164 match = pat.search(actual)
165 if not match:
166 self.fail("Log line does not match expected pattern:\n" +
167 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000168 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000169 s = stream.read()
170 if s:
171 self.fail("Remaining output at end of log stream:\n" + s)
172
173 def next_message(self):
174 """Generate a message consisting solely of an auto-incrementing
175 integer."""
176 self.message_num += 1
177 return "%d" % self.message_num
178
179
180class BuiltinLevelsTest(BaseTest):
181 """Test builtin levels and their inheritance."""
182
183 def test_flat(self):
184 #Logging levels in a flat logger namespace.
185 m = self.next_message
186
187 ERR = logging.getLogger("ERR")
188 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000189 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000190 INF.setLevel(logging.INFO)
191 DEB = logging.getLogger("DEB")
192 DEB.setLevel(logging.DEBUG)
193
194 # These should log.
195 ERR.log(logging.CRITICAL, m())
196 ERR.error(m())
197
198 INF.log(logging.CRITICAL, m())
199 INF.error(m())
200 INF.warn(m())
201 INF.info(m())
202
203 DEB.log(logging.CRITICAL, m())
204 DEB.error(m())
205 DEB.warn (m())
206 DEB.info (m())
207 DEB.debug(m())
208
209 # These should not log.
210 ERR.warn(m())
211 ERR.info(m())
212 ERR.debug(m())
213
214 INF.debug(m())
215
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000216 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000217 ('ERR', 'CRITICAL', '1'),
218 ('ERR', 'ERROR', '2'),
219 ('INF', 'CRITICAL', '3'),
220 ('INF', 'ERROR', '4'),
221 ('INF', 'WARNING', '5'),
222 ('INF', 'INFO', '6'),
223 ('DEB', 'CRITICAL', '7'),
224 ('DEB', 'ERROR', '8'),
225 ('DEB', 'WARNING', '9'),
226 ('DEB', 'INFO', '10'),
227 ('DEB', 'DEBUG', '11'),
228 ])
229
230 def test_nested_explicit(self):
231 # Logging levels in a nested namespace, all explicitly set.
232 m = self.next_message
233
234 INF = logging.getLogger("INF")
235 INF.setLevel(logging.INFO)
236 INF_ERR = logging.getLogger("INF.ERR")
237 INF_ERR.setLevel(logging.ERROR)
238
239 # These should log.
240 INF_ERR.log(logging.CRITICAL, m())
241 INF_ERR.error(m())
242
243 # These should not log.
244 INF_ERR.warn(m())
245 INF_ERR.info(m())
246 INF_ERR.debug(m())
247
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000248 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000249 ('INF.ERR', 'CRITICAL', '1'),
250 ('INF.ERR', 'ERROR', '2'),
251 ])
252
253 def test_nested_inherited(self):
254 #Logging levels in a nested namespace, inherited from parent loggers.
255 m = self.next_message
256
257 INF = logging.getLogger("INF")
258 INF.setLevel(logging.INFO)
259 INF_ERR = logging.getLogger("INF.ERR")
260 INF_ERR.setLevel(logging.ERROR)
261 INF_UNDEF = logging.getLogger("INF.UNDEF")
262 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
263 UNDEF = logging.getLogger("UNDEF")
264
265 # These should log.
266 INF_UNDEF.log(logging.CRITICAL, m())
267 INF_UNDEF.error(m())
268 INF_UNDEF.warn(m())
269 INF_UNDEF.info(m())
270 INF_ERR_UNDEF.log(logging.CRITICAL, m())
271 INF_ERR_UNDEF.error(m())
272
273 # These should not log.
274 INF_UNDEF.debug(m())
275 INF_ERR_UNDEF.warn(m())
276 INF_ERR_UNDEF.info(m())
277 INF_ERR_UNDEF.debug(m())
278
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000279 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000280 ('INF.UNDEF', 'CRITICAL', '1'),
281 ('INF.UNDEF', 'ERROR', '2'),
282 ('INF.UNDEF', 'WARNING', '3'),
283 ('INF.UNDEF', 'INFO', '4'),
284 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
285 ('INF.ERR.UNDEF', 'ERROR', '6'),
286 ])
287
288 def test_nested_with_virtual_parent(self):
289 # Logging levels when some parent does not exist yet.
290 m = self.next_message
291
292 INF = logging.getLogger("INF")
293 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
294 CHILD = logging.getLogger("INF.BADPARENT")
295 INF.setLevel(logging.INFO)
296
297 # These should log.
298 GRANDCHILD.log(logging.FATAL, m())
299 GRANDCHILD.info(m())
300 CHILD.log(logging.FATAL, m())
301 CHILD.info(m())
302
303 # These should not log.
304 GRANDCHILD.debug(m())
305 CHILD.debug(m())
306
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000307 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000308 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
309 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
310 ('INF.BADPARENT', 'CRITICAL', '3'),
311 ('INF.BADPARENT', 'INFO', '4'),
312 ])
313
314
315class BasicFilterTest(BaseTest):
316
317 """Test the bundled Filter class."""
318
319 def test_filter(self):
320 # Only messages satisfying the specified criteria pass through the
321 # filter.
322 filter_ = logging.Filter("spam.eggs")
323 handler = self.root_logger.handlers[0]
324 try:
325 handler.addFilter(filter_)
326 spam = logging.getLogger("spam")
327 spam_eggs = logging.getLogger("spam.eggs")
328 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
329 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
330
331 spam.info(self.next_message())
332 spam_eggs.info(self.next_message()) # Good.
333 spam_eggs_fish.info(self.next_message()) # Good.
334 spam_bakedbeans.info(self.next_message())
335
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000336 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000337 ('spam.eggs', 'INFO', '2'),
338 ('spam.eggs.fish', 'INFO', '3'),
339 ])
340 finally:
341 handler.removeFilter(filter_)
342
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000343 def test_callable_filter(self):
344 # Only messages satisfying the specified criteria pass through the
345 # filter.
346
347 def filterfunc(record):
348 parts = record.name.split('.')
349 prefix = '.'.join(parts[:2])
350 return prefix == 'spam.eggs'
351
352 handler = self.root_logger.handlers[0]
353 try:
354 handler.addFilter(filterfunc)
355 spam = logging.getLogger("spam")
356 spam_eggs = logging.getLogger("spam.eggs")
357 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
358 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
359
360 spam.info(self.next_message())
361 spam_eggs.info(self.next_message()) # Good.
362 spam_eggs_fish.info(self.next_message()) # Good.
363 spam_bakedbeans.info(self.next_message())
364
365 self.assert_log_lines([
366 ('spam.eggs', 'INFO', '2'),
367 ('spam.eggs.fish', 'INFO', '3'),
368 ])
369 finally:
370 handler.removeFilter(filterfunc)
371
Vinay Sajip985ef872011-04-26 19:34:04 +0100372 def test_empty_filter(self):
373 f = logging.Filter()
374 r = logging.makeLogRecord({'name': 'spam.eggs'})
375 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000376
377#
378# First, we define our levels. There can be as many as you want - the only
379# limitations are that they should be integers, the lowest should be > 0 and
380# larger values mean less information being logged. If you need specific
381# level values which do not fit into these limitations, you can use a
382# mapping dictionary to convert between your application levels and the
383# logging system.
384#
385SILENT = 120
386TACITURN = 119
387TERSE = 118
388EFFUSIVE = 117
389SOCIABLE = 116
390VERBOSE = 115
391TALKATIVE = 114
392GARRULOUS = 113
393CHATTERBOX = 112
394BORING = 111
395
396LEVEL_RANGE = range(BORING, SILENT + 1)
397
398#
399# Next, we define names for our levels. You don't need to do this - in which
400# case the system will use "Level n" to denote the text for the level.
401#
402my_logging_levels = {
403 SILENT : 'Silent',
404 TACITURN : 'Taciturn',
405 TERSE : 'Terse',
406 EFFUSIVE : 'Effusive',
407 SOCIABLE : 'Sociable',
408 VERBOSE : 'Verbose',
409 TALKATIVE : 'Talkative',
410 GARRULOUS : 'Garrulous',
411 CHATTERBOX : 'Chatterbox',
412 BORING : 'Boring',
413}
414
415class GarrulousFilter(logging.Filter):
416
417 """A filter which blocks garrulous messages."""
418
419 def filter(self, record):
420 return record.levelno != GARRULOUS
421
422class VerySpecificFilter(logging.Filter):
423
424 """A filter which blocks sociable and taciturn messages."""
425
426 def filter(self, record):
427 return record.levelno not in [SOCIABLE, TACITURN]
428
429
430class CustomLevelsAndFiltersTest(BaseTest):
431
432 """Test various filtering possibilities with custom logging levels."""
433
434 # Skip the logger name group.
435 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
436
437 def setUp(self):
438 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000439 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000440 logging.addLevelName(k, v)
441
442 def log_at_all_levels(self, logger):
443 for lvl in LEVEL_RANGE:
444 logger.log(lvl, self.next_message())
445
446 def test_logger_filter(self):
447 # Filter at logger level.
448 self.root_logger.setLevel(VERBOSE)
449 # Levels >= 'Verbose' are good.
450 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000451 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000452 ('Verbose', '5'),
453 ('Sociable', '6'),
454 ('Effusive', '7'),
455 ('Terse', '8'),
456 ('Taciturn', '9'),
457 ('Silent', '10'),
458 ])
459
460 def test_handler_filter(self):
461 # Filter at handler level.
462 self.root_logger.handlers[0].setLevel(SOCIABLE)
463 try:
464 # Levels >= 'Sociable' are good.
465 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000466 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000467 ('Sociable', '6'),
468 ('Effusive', '7'),
469 ('Terse', '8'),
470 ('Taciturn', '9'),
471 ('Silent', '10'),
472 ])
473 finally:
474 self.root_logger.handlers[0].setLevel(logging.NOTSET)
475
476 def test_specific_filters(self):
477 # Set a specific filter object on the handler, and then add another
478 # filter object on the logger itself.
479 handler = self.root_logger.handlers[0]
480 specific_filter = None
481 garr = GarrulousFilter()
482 handler.addFilter(garr)
483 try:
484 self.log_at_all_levels(self.root_logger)
485 first_lines = [
486 # Notice how 'Garrulous' is missing
487 ('Boring', '1'),
488 ('Chatterbox', '2'),
489 ('Talkative', '4'),
490 ('Verbose', '5'),
491 ('Sociable', '6'),
492 ('Effusive', '7'),
493 ('Terse', '8'),
494 ('Taciturn', '9'),
495 ('Silent', '10'),
496 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000497 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000498
499 specific_filter = VerySpecificFilter()
500 self.root_logger.addFilter(specific_filter)
501 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000502 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000503 # Not only 'Garrulous' is still missing, but also 'Sociable'
504 # and 'Taciturn'
505 ('Boring', '11'),
506 ('Chatterbox', '12'),
507 ('Talkative', '14'),
508 ('Verbose', '15'),
509 ('Effusive', '17'),
510 ('Terse', '18'),
511 ('Silent', '20'),
512 ])
513 finally:
514 if specific_filter:
515 self.root_logger.removeFilter(specific_filter)
516 handler.removeFilter(garr)
517
518
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100519class HandlerTest(BaseTest):
520 def test_name(self):
521 h = logging.Handler()
522 h.name = 'generic'
523 self.assertEqual(h.name, 'generic')
524 h.name = 'anothergeneric'
525 self.assertEqual(h.name, 'anothergeneric')
526 self.assertRaises(NotImplementedError, h.emit, None)
527
Vinay Sajip5a35b062011-04-27 11:31:14 +0100528 def test_builtin_handlers(self):
529 # We can't actually *use* too many handlers in the tests,
530 # but we can try instantiating them with various options
531 if sys.platform in ('linux2', 'darwin'):
532 for existing in (True, False):
533 fd, fn = tempfile.mkstemp()
534 os.close(fd)
535 if not existing:
536 os.unlink(fn)
537 h = logging.handlers.WatchedFileHandler(fn, delay=True)
538 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100539 dev, ino = h.dev, h.ino
540 self.assertNotEqual(dev, -1)
541 self.assertNotEqual(ino, -1)
542 r = logging.makeLogRecord({'msg': 'Test'})
543 h.handle(r)
544 # Now remove the file.
545 os.unlink(fn)
546 self.assertFalse(os.path.exists(fn))
547 # The next call should recreate the file.
548 h.handle(r)
549 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100550 else:
551 self.assertEqual(h.dev, -1)
552 self.assertEqual(h.ino, -1)
553 h.close()
554 if existing:
555 os.unlink(fn)
556 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100557 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100558 else:
559 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100560 try:
561 h = logging.handlers.SysLogHandler(sockname)
562 self.assertEqual(h.facility, h.LOG_USER)
563 self.assertTrue(h.unixsocket)
564 h.close()
565 except socket.error: # syslogd might not be available
566 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100567 for method in ('GET', 'POST', 'PUT'):
568 if method == 'PUT':
569 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
570 'localhost', '/log', method)
571 else:
572 h = logging.handlers.HTTPHandler('localhost', '/log', method)
573 h.close()
574 h = logging.handlers.BufferingHandler(0)
575 r = logging.makeLogRecord({})
576 self.assertTrue(h.shouldFlush(r))
577 h.close()
578 h = logging.handlers.BufferingHandler(1)
579 self.assertFalse(h.shouldFlush(r))
580 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100581
582class BadStream(object):
583 def write(self, data):
584 raise RuntimeError('deliberate mistake')
585
586class TestStreamHandler(logging.StreamHandler):
587 def handleError(self, record):
588 self.error_record = record
589
590class StreamHandlerTest(BaseTest):
591 def test_error_handling(self):
592 h = TestStreamHandler(BadStream())
593 r = logging.makeLogRecord({})
594 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100595 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100596 try:
597 h.handle(r)
598 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100599 h = logging.StreamHandler(BadStream())
600 sys.stderr = sio = io.StringIO()
601 h.handle(r)
602 self.assertTrue('\nRuntimeError: '
603 'deliberate mistake\n' in sio.getvalue())
604 logging.raiseExceptions = False
605 sys.stderr = sio = io.StringIO()
606 h.handle(r)
607 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100608 finally:
609 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100610 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100611
Vinay Sajip7367d082011-05-02 13:17:27 +0100612# -- The following section could be moved into a server_helper.py module
613# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100614
Vinay Sajipce7c9782011-05-17 07:15:53 +0100615if threading:
616 class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100617 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100618 This derived class has had to be created because smtpd does not
619 support use of custom channel maps, although they are allowed by
620 asyncore's design. Issue #11959 has been raised to address this,
621 and if resolved satisfactorily, some of this code can be removed.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100622 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100623 def __init__(self, server, conn, addr, sockmap):
624 asynchat.async_chat.__init__(self, conn, sockmap)
625 self.smtp_server = server
626 self.conn = conn
627 self.addr = addr
628 self.received_lines = []
629 self.smtp_state = self.COMMAND
630 self.seen_greeting = ''
631 self.mailfrom = None
632 self.rcpttos = []
633 self.received_data = ''
634 self.fqdn = socket.getfqdn()
635 self.num_bytes = 0
636 try:
637 self.peer = conn.getpeername()
638 except socket.error as err:
639 # a race condition may occur if the other end is closing
640 # before we can get the peername
641 self.close()
642 if err.args[0] != errno.ENOTCONN:
643 raise
644 return
645 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
646 self.set_terminator(b'\r\n')
Vinay Sajipa463d252011-04-30 21:52:48 +0100647
Vinay Sajip314b92b2011-05-02 14:31:16 +0100648
Vinay Sajipce7c9782011-05-17 07:15:53 +0100649 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100650 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100651 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100652
Vinay Sajipce7c9782011-05-17 07:15:53 +0100653 :param addr: A (host, port) tuple which the server listens on.
654 You can specify a port value of zero: the server's
655 *port* attribute will hold the actual port number
656 used, which can be used in client connections.
657 :param handler: A callable which will be called to process
658 incoming messages. The handler will be passed
659 the client address tuple, who the message is from,
660 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100661 :param poll_interval: The interval, in seconds, used in the underlying
662 :func:`select` or :func:`poll` call by
663 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100664 :param sockmap: A dictionary which will be used to hold
665 :class:`asyncore.dispatcher` instances used by
666 :func:`asyncore.loop`. This avoids changing the
667 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100668 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100669 channel_class = TestSMTPChannel
670
671 def __init__(self, addr, handler, poll_interval, sockmap):
672 self._localaddr = addr
673 self._remoteaddr = None
674 self.sockmap = sockmap
675 asyncore.dispatcher.__init__(self, map=sockmap)
676 try:
677 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
678 sock.setblocking(0)
679 self.set_socket(sock, map=sockmap)
680 # try to re-use a server port if possible
681 self.set_reuse_addr()
682 self.bind(addr)
683 self.port = sock.getsockname()[1]
684 self.listen(5)
685 except:
686 self.close()
Vinay Sajipe73afad2011-05-10 07:48:28 +0100687 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +0100688 self._handler = handler
689 self._thread = None
690 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100691
Vinay Sajipce7c9782011-05-17 07:15:53 +0100692 def handle_accepted(self, conn, addr):
693 """
694 Redefined only because the base class does not pass in a
695 map, forcing use of a global in :mod:`asyncore`.
696 """
697 channel = self.channel_class(self, conn, addr, self.sockmap)
Vinay Sajip314b92b2011-05-02 14:31:16 +0100698
Vinay Sajipce7c9782011-05-17 07:15:53 +0100699 def process_message(self, peer, mailfrom, rcpttos, data):
700 """
701 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100702
Vinay Sajipce7c9782011-05-17 07:15:53 +0100703 Typically, this will be a test case method.
704 :param peer: The client (host, port) tuple.
705 :param mailfrom: The address of the sender.
706 :param rcpttos: The addresses of the recipients.
707 :param data: The message.
708 """
709 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100710
Vinay Sajipce7c9782011-05-17 07:15:53 +0100711 def start(self):
712 """
713 Start the server running on a separate daemon thread.
714 """
715 self._thread = t = threading.Thread(target=self.serve_forever,
716 args=(self.poll_interval,))
717 t.setDaemon(True)
718 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100719
Vinay Sajipce7c9782011-05-17 07:15:53 +0100720 def serve_forever(self, poll_interval):
721 """
722 Run the :mod:`asyncore` loop until normal termination
723 conditions arise.
724 :param poll_interval: The interval, in seconds, used in the underlying
725 :func:`select` or :func:`poll` call by
726 :func:`asyncore.loop`.
727 """
728 try:
729 asyncore.loop(poll_interval, map=self.sockmap)
730 except select.error:
731 # On FreeBSD 8, closing the server repeatably
732 # raises this error. We swallow it if the
733 # server has been closed.
734 if self.connected or self.accepting:
735 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100736
Vinay Sajipce7c9782011-05-17 07:15:53 +0100737 def stop(self, timeout=None):
738 """
739 Stop the thread by closing the server instance.
740 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100741
Vinay Sajipce7c9782011-05-17 07:15:53 +0100742 :param timeout: How long to wait for the server thread
743 to terminate.
744 """
745 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100746 self._thread.join(timeout)
747 self._thread = None
Vinay Sajip7367d082011-05-02 13:17:27 +0100748
Vinay Sajipce7c9782011-05-17 07:15:53 +0100749 class ControlMixin(object):
750 """
751 This mixin is used to start a server on a separate thread, and
752 shut it down programmatically. Request handling is simplified - instead
753 of needing to derive a suitable RequestHandler subclass, you just
754 provide a callable which will be passed each received request to be
755 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100756
Vinay Sajipce7c9782011-05-17 07:15:53 +0100757 :param handler: A handler callable which will be called with a
758 single parameter - the request - in order to
759 process the request. This handler is called on the
760 server thread, effectively meaning that requests are
761 processed serially. While not quite Web scale ;-),
762 this should be fine for testing applications.
763 :param poll_interval: The polling interval in seconds.
764 """
765 def __init__(self, handler, poll_interval):
766 self._thread = None
767 self.poll_interval = poll_interval
768 self._handler = handler
769 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100770
Vinay Sajipce7c9782011-05-17 07:15:53 +0100771 def start(self):
772 """
773 Create a daemon thread to run the server, and start it.
774 """
775 self._thread = t = threading.Thread(target=self.serve_forever,
776 args=(self.poll_interval,))
777 t.setDaemon(True)
778 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100779
Vinay Sajipce7c9782011-05-17 07:15:53 +0100780 def serve_forever(self, poll_interval):
781 """
782 Run the server. Set the ready flag before entering the
783 service loop.
784 """
785 self.ready.set()
786 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100787
Vinay Sajipce7c9782011-05-17 07:15:53 +0100788 def stop(self, timeout=None):
789 """
790 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100791
Vinay Sajipce7c9782011-05-17 07:15:53 +0100792 :param timeout: How long to wait for the server thread
793 to terminate.
794 """
795 self.shutdown()
796 if self._thread is not None:
797 self._thread.join(timeout)
798 self._thread = None
799 self.server_close()
800 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100801
Vinay Sajipce7c9782011-05-17 07:15:53 +0100802 class TestHTTPServer(ControlMixin, HTTPServer):
803 """
804 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100805
Vinay Sajipce7c9782011-05-17 07:15:53 +0100806 :param addr: A tuple with the IP address and port to listen on.
807 :param handler: A handler callable which will be called with a
808 single parameter - the request - in order to
809 process the request.
810 :param poll_interval: The polling interval in seconds.
811 :param log: Pass ``True`` to enable log messages.
812 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100813 def __init__(self, addr, handler, poll_interval=0.5,
814 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100815 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
816 def __getattr__(self, name, default=None):
817 if name.startswith('do_'):
818 return self.process_request
819 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100820
Vinay Sajipce7c9782011-05-17 07:15:53 +0100821 def process_request(self):
822 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100823
Vinay Sajipce7c9782011-05-17 07:15:53 +0100824 def log_message(self, format, *args):
825 if log:
826 super(DelegatingHTTPRequestHandler,
827 self).log_message(format, *args)
828 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
829 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100830 self.sslctx = sslctx
831
832 def get_request(self):
833 try:
834 sock, addr = self.socket.accept()
835 if self.sslctx:
836 sock = self.sslctx.wrap_socket(sock, server_side=True)
837 except socket.error as e:
838 # socket errors are silenced by the caller, print them here
839 sys.stderr.write("Got an error:\n%s\n" % e)
840 raise
841 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100842
Vinay Sajipce7c9782011-05-17 07:15:53 +0100843 class TestTCPServer(ControlMixin, ThreadingTCPServer):
844 """
845 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100846
Vinay Sajipce7c9782011-05-17 07:15:53 +0100847 :param addr: A tuple with the IP address and port to listen on.
848 :param handler: A handler callable which will be called with a single
849 parameter - the request - in order to process the request.
850 :param poll_interval: The polling interval in seconds.
851 :bind_and_activate: If True (the default), binds the server and starts it
852 listening. If False, you need to call
853 :meth:`server_bind` and :meth:`server_activate` at
854 some later time before calling :meth:`start`, so that
855 the server will set up the socket and listen on it.
856 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100857
Vinay Sajipce7c9782011-05-17 07:15:53 +0100858 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100859
Vinay Sajipce7c9782011-05-17 07:15:53 +0100860 def __init__(self, addr, handler, poll_interval=0.5,
861 bind_and_activate=True):
862 class DelegatingTCPRequestHandler(StreamRequestHandler):
863
864 def handle(self):
865 self.server._handler(self)
866 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
867 bind_and_activate)
868 ControlMixin.__init__(self, handler, poll_interval)
869
870 def server_bind(self):
871 super(TestTCPServer, self).server_bind()
872 self.port = self.socket.getsockname()[1]
873
874 class TestUDPServer(ControlMixin, ThreadingUDPServer):
875 """
876 A UDP server which is controllable using :class:`ControlMixin`.
877
878 :param addr: A tuple with the IP address and port to listen on.
879 :param handler: A handler callable which will be called with a
880 single parameter - the request - in order to
881 process the request.
882 :param poll_interval: The polling interval for shutdown requests,
883 in seconds.
884 :bind_and_activate: If True (the default), binds the server and
885 starts it listening. If False, you need to
886 call :meth:`server_bind` and
887 :meth:`server_activate` at some later time
888 before calling :meth:`start`, so that the server will
889 set up the socket and listen on it.
890 """
891 def __init__(self, addr, handler, poll_interval=0.5, bind_and_activate=True):
892 class DelegatingUDPRequestHandler(DatagramRequestHandler):
893
894 def handle(self):
895 self.server._handler(self)
896 ThreadingUDPServer.__init__(self, addr, DelegatingUDPRequestHandler,
897 bind_and_activate)
898 ControlMixin.__init__(self, handler, poll_interval)
899
900 def server_bind(self):
901 super(TestUDPServer, self).server_bind()
902 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100903
904
905# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100906
Vinay Sajipce7c9782011-05-17 07:15:53 +0100907@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100908class SMTPHandlerTest(BaseTest):
909 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100910 sockmap = {}
911 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
912 sockmap)
913 server.start()
914 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100915 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
916 self.assertEqual(h.toaddrs, ['you'])
917 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100918 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100919 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100920 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100921 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100922 server.stop()
923 self.assertEqual(len(self.messages), 1)
924 peer, mailfrom, rcpttos, data = self.messages[0]
925 self.assertEqual(mailfrom, 'me')
926 self.assertEqual(rcpttos, ['you'])
927 self.assertTrue('\nSubject: Log\n' in data)
928 self.assertTrue(data.endswith('\n\nHello'))
929 h.close()
930
931 def process_message(self, *args):
932 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100933 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100934
Christian Heimes180510d2008-03-03 19:15:45 +0000935class MemoryHandlerTest(BaseTest):
936
937 """Tests for the MemoryHandler."""
938
939 # Do not bother with a logger name group.
940 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
941
942 def setUp(self):
943 BaseTest.setUp(self)
944 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
945 self.root_hdlr)
946 self.mem_logger = logging.getLogger('mem')
947 self.mem_logger.propagate = 0
948 self.mem_logger.addHandler(self.mem_hdlr)
949
950 def tearDown(self):
951 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000952 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000953
954 def test_flush(self):
955 # The memory handler flushes to its target handler based on specific
956 # criteria (message count and message level).
957 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000958 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000959 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000960 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000961 # This will flush because the level is >= logging.WARNING
962 self.mem_logger.warn(self.next_message())
963 lines = [
964 ('DEBUG', '1'),
965 ('INFO', '2'),
966 ('WARNING', '3'),
967 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000968 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000969 for n in (4, 14):
970 for i in range(9):
971 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000972 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000973 # This will flush because it's the 10th message since the last
974 # flush.
975 self.mem_logger.debug(self.next_message())
976 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000977 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000978
979 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000980 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000981
982
983class ExceptionFormatter(logging.Formatter):
984 """A special exception formatter."""
985 def formatException(self, ei):
986 return "Got a [%s]" % ei[0].__name__
987
988
989class ConfigFileTest(BaseTest):
990
991 """Reading logging config from a .ini-style config file."""
992
993 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
994
995 # config0 is a standard configuration.
996 config0 = """
997 [loggers]
998 keys=root
999
1000 [handlers]
1001 keys=hand1
1002
1003 [formatters]
1004 keys=form1
1005
1006 [logger_root]
1007 level=WARNING
1008 handlers=hand1
1009
1010 [handler_hand1]
1011 class=StreamHandler
1012 level=NOTSET
1013 formatter=form1
1014 args=(sys.stdout,)
1015
1016 [formatter_form1]
1017 format=%(levelname)s ++ %(message)s
1018 datefmt=
1019 """
1020
1021 # config1 adds a little to the standard configuration.
1022 config1 = """
1023 [loggers]
1024 keys=root,parser
1025
1026 [handlers]
1027 keys=hand1
1028
1029 [formatters]
1030 keys=form1
1031
1032 [logger_root]
1033 level=WARNING
1034 handlers=
1035
1036 [logger_parser]
1037 level=DEBUG
1038 handlers=hand1
1039 propagate=1
1040 qualname=compiler.parser
1041
1042 [handler_hand1]
1043 class=StreamHandler
1044 level=NOTSET
1045 formatter=form1
1046 args=(sys.stdout,)
1047
1048 [formatter_form1]
1049 format=%(levelname)s ++ %(message)s
1050 datefmt=
1051 """
1052
Vinay Sajip3f84b072011-03-07 17:49:33 +00001053 # config1a moves the handler to the root.
1054 config1a = """
1055 [loggers]
1056 keys=root,parser
1057
1058 [handlers]
1059 keys=hand1
1060
1061 [formatters]
1062 keys=form1
1063
1064 [logger_root]
1065 level=WARNING
1066 handlers=hand1
1067
1068 [logger_parser]
1069 level=DEBUG
1070 handlers=
1071 propagate=1
1072 qualname=compiler.parser
1073
1074 [handler_hand1]
1075 class=StreamHandler
1076 level=NOTSET
1077 formatter=form1
1078 args=(sys.stdout,)
1079
1080 [formatter_form1]
1081 format=%(levelname)s ++ %(message)s
1082 datefmt=
1083 """
1084
Christian Heimes180510d2008-03-03 19:15:45 +00001085 # config2 has a subtle configuration error that should be reported
1086 config2 = config1.replace("sys.stdout", "sys.stbout")
1087
1088 # config3 has a less subtle configuration error
1089 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1090
1091 # config4 specifies a custom formatter class to be loaded
1092 config4 = """
1093 [loggers]
1094 keys=root
1095
1096 [handlers]
1097 keys=hand1
1098
1099 [formatters]
1100 keys=form1
1101
1102 [logger_root]
1103 level=NOTSET
1104 handlers=hand1
1105
1106 [handler_hand1]
1107 class=StreamHandler
1108 level=NOTSET
1109 formatter=form1
1110 args=(sys.stdout,)
1111
1112 [formatter_form1]
1113 class=""" + __name__ + """.ExceptionFormatter
1114 format=%(levelname)s:%(name)s:%(message)s
1115 datefmt=
1116 """
1117
Georg Brandl3dbca812008-07-23 16:10:53 +00001118 # config5 specifies a custom handler class to be loaded
1119 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1120
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001121 # config6 uses ', ' delimiters in the handlers and formatters sections
1122 config6 = """
1123 [loggers]
1124 keys=root,parser
1125
1126 [handlers]
1127 keys=hand1, hand2
1128
1129 [formatters]
1130 keys=form1, form2
1131
1132 [logger_root]
1133 level=WARNING
1134 handlers=
1135
1136 [logger_parser]
1137 level=DEBUG
1138 handlers=hand1
1139 propagate=1
1140 qualname=compiler.parser
1141
1142 [handler_hand1]
1143 class=StreamHandler
1144 level=NOTSET
1145 formatter=form1
1146 args=(sys.stdout,)
1147
1148 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001149 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001150 level=NOTSET
1151 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001152 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001153
1154 [formatter_form1]
1155 format=%(levelname)s ++ %(message)s
1156 datefmt=
1157
1158 [formatter_form2]
1159 format=%(message)s
1160 datefmt=
1161 """
1162
Vinay Sajip3f84b072011-03-07 17:49:33 +00001163 # config7 adds a compiler logger.
1164 config7 = """
1165 [loggers]
1166 keys=root,parser,compiler
1167
1168 [handlers]
1169 keys=hand1
1170
1171 [formatters]
1172 keys=form1
1173
1174 [logger_root]
1175 level=WARNING
1176 handlers=hand1
1177
1178 [logger_compiler]
1179 level=DEBUG
1180 handlers=
1181 propagate=1
1182 qualname=compiler
1183
1184 [logger_parser]
1185 level=DEBUG
1186 handlers=
1187 propagate=1
1188 qualname=compiler.parser
1189
1190 [handler_hand1]
1191 class=StreamHandler
1192 level=NOTSET
1193 formatter=form1
1194 args=(sys.stdout,)
1195
1196 [formatter_form1]
1197 format=%(levelname)s ++ %(message)s
1198 datefmt=
1199 """
1200
Christian Heimes180510d2008-03-03 19:15:45 +00001201 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001202 file = io.StringIO(textwrap.dedent(conf))
1203 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001204
1205 def test_config0_ok(self):
1206 # A simple config file which overrides the default settings.
1207 with captured_stdout() as output:
1208 self.apply_config(self.config0)
1209 logger = logging.getLogger()
1210 # Won't output anything
1211 logger.info(self.next_message())
1212 # Outputs a message
1213 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001214 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001215 ('ERROR', '2'),
1216 ], stream=output)
1217 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001218 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001219
Georg Brandl3dbca812008-07-23 16:10:53 +00001220 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001221 # A config file defining a sub-parser as well.
1222 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001223 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001224 logger = logging.getLogger("compiler.parser")
1225 # Both will output a message
1226 logger.info(self.next_message())
1227 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001228 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001229 ('INFO', '1'),
1230 ('ERROR', '2'),
1231 ], stream=output)
1232 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001233 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001234
1235 def test_config2_failure(self):
1236 # A simple config file which overrides the default settings.
1237 self.assertRaises(Exception, self.apply_config, self.config2)
1238
1239 def test_config3_failure(self):
1240 # A simple config file which overrides the default settings.
1241 self.assertRaises(Exception, self.apply_config, self.config3)
1242
1243 def test_config4_ok(self):
1244 # A config file specifying a custom formatter class.
1245 with captured_stdout() as output:
1246 self.apply_config(self.config4)
1247 logger = logging.getLogger()
1248 try:
1249 raise RuntimeError()
1250 except RuntimeError:
1251 logging.exception("just testing")
1252 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001253 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001254 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1255 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001256 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001257
Georg Brandl3dbca812008-07-23 16:10:53 +00001258 def test_config5_ok(self):
1259 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001260
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001261 def test_config6_ok(self):
1262 self.test_config1_ok(config=self.config6)
1263
Vinay Sajip3f84b072011-03-07 17:49:33 +00001264 def test_config7_ok(self):
1265 with captured_stdout() as output:
1266 self.apply_config(self.config1a)
1267 logger = logging.getLogger("compiler.parser")
1268 # See issue #11424. compiler-hyphenated sorts
1269 # between compiler and compiler.xyz and this
1270 # was preventing compiler.xyz from being included
1271 # in the child loggers of compiler because of an
1272 # overzealous loop termination condition.
1273 hyphenated = logging.getLogger('compiler-hyphenated')
1274 # All will output a message
1275 logger.info(self.next_message())
1276 logger.error(self.next_message())
1277 hyphenated.critical(self.next_message())
1278 self.assert_log_lines([
1279 ('INFO', '1'),
1280 ('ERROR', '2'),
1281 ('CRITICAL', '3'),
1282 ], stream=output)
1283 # Original logger output is empty.
1284 self.assert_log_lines([])
1285 with captured_stdout() as output:
1286 self.apply_config(self.config7)
1287 logger = logging.getLogger("compiler.parser")
1288 self.assertFalse(logger.disabled)
1289 # Both will output a message
1290 logger.info(self.next_message())
1291 logger.error(self.next_message())
1292 logger = logging.getLogger("compiler.lexer")
1293 # Both will output a message
1294 logger.info(self.next_message())
1295 logger.error(self.next_message())
1296 # Will not appear
1297 hyphenated.critical(self.next_message())
1298 self.assert_log_lines([
1299 ('INFO', '4'),
1300 ('ERROR', '5'),
1301 ('INFO', '6'),
1302 ('ERROR', '7'),
1303 ], stream=output)
1304 # Original logger output is empty.
1305 self.assert_log_lines([])
1306
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001307
Victor Stinner45df8202010-04-28 22:31:17 +00001308@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001309class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001310
Christian Heimes180510d2008-03-03 19:15:45 +00001311 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001312
Christian Heimes180510d2008-03-03 19:15:45 +00001313 def setUp(self):
1314 """Set up a TCP server to receive log messages, and a SocketHandler
1315 pointing to that server's address and port."""
1316 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001317 addr = ('localhost', 0)
1318 self.server = server = TestTCPServer(addr, self.handle_socket,
1319 0.01)
1320 server.start()
1321 server.ready.wait()
1322 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1323 server.port)
1324 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001325 self.root_logger.removeHandler(self.root_logger.handlers[0])
1326 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001327 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001328
Christian Heimes180510d2008-03-03 19:15:45 +00001329 def tearDown(self):
1330 """Shutdown the TCP server."""
1331 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001332 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001333 self.root_logger.removeHandler(self.sock_hdlr)
1334 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001335 finally:
1336 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001337
Vinay Sajip7367d082011-05-02 13:17:27 +01001338 def handle_socket(self, request):
1339 conn = request.connection
1340 while True:
1341 chunk = conn.recv(4)
1342 if len(chunk) < 4:
1343 break
1344 slen = struct.unpack(">L", chunk)[0]
1345 chunk = conn.recv(slen)
1346 while len(chunk) < slen:
1347 chunk = chunk + conn.recv(slen - len(chunk))
1348 obj = pickle.loads(chunk)
1349 record = logging.makeLogRecord(obj)
1350 self.log_output += record.msg + '\n'
1351 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001352
Christian Heimes180510d2008-03-03 19:15:45 +00001353 def test_output(self):
1354 # The log message sent to the SocketHandler is properly received.
1355 logger = logging.getLogger("tcp")
1356 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001357 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001358 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001359 self.handled.acquire()
1360 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001361
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001362 def test_noserver(self):
1363 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001364 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001365 #The logging call should try to connect, which should fail
1366 try:
1367 raise RuntimeError('Deliberate mistake')
1368 except RuntimeError:
1369 self.root_logger.exception('Never sent')
1370 self.root_logger.error('Never sent, either')
1371 now = time.time()
1372 self.assertTrue(self.sock_hdlr.retryTime > now)
1373 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1374 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001375
Vinay Sajip7367d082011-05-02 13:17:27 +01001376
1377@unittest.skipUnless(threading, 'Threading required for this test.')
1378class DatagramHandlerTest(BaseTest):
1379
1380 """Test for DatagramHandler."""
1381
1382 def setUp(self):
1383 """Set up a UDP server to receive log messages, and a DatagramHandler
1384 pointing to that server's address and port."""
1385 BaseTest.setUp(self)
1386 addr = ('localhost', 0)
1387 self.server = server = TestUDPServer(addr, self.handle_datagram,
1388 0.01)
1389 server.start()
1390 server.ready.wait()
1391 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1392 server.port)
1393 self.log_output = ''
1394 self.root_logger.removeHandler(self.root_logger.handlers[0])
1395 self.root_logger.addHandler(self.sock_hdlr)
1396 self.handled = threading.Event()
1397
1398 def tearDown(self):
1399 """Shutdown the UDP server."""
1400 try:
1401 self.server.stop(2.0)
1402 self.root_logger.removeHandler(self.sock_hdlr)
1403 self.sock_hdlr.close()
1404 finally:
1405 BaseTest.tearDown(self)
1406
1407 def handle_datagram(self, request):
1408 slen = struct.pack('>L', 0) # length of prefix
1409 packet = request.packet[len(slen):]
1410 obj = pickle.loads(packet)
1411 record = logging.makeLogRecord(obj)
1412 self.log_output += record.msg + '\n'
1413 self.handled.set()
1414
1415 def test_output(self):
1416 # The log message sent to the DatagramHandler is properly received.
1417 logger = logging.getLogger("udp")
1418 logger.error("spam")
1419 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001420 self.handled.clear()
1421 logger.error("eggs")
1422 self.handled.wait()
1423 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001424
1425
1426@unittest.skipUnless(threading, 'Threading required for this test.')
1427class SysLogHandlerTest(BaseTest):
1428
1429 """Test for SysLogHandler using UDP."""
1430
1431 def setUp(self):
1432 """Set up a UDP server to receive log messages, and a SysLogHandler
1433 pointing to that server's address and port."""
1434 BaseTest.setUp(self)
1435 addr = ('localhost', 0)
1436 self.server = server = TestUDPServer(addr, self.handle_datagram,
1437 0.01)
1438 server.start()
1439 server.ready.wait()
1440 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1441 server.port))
1442 self.log_output = ''
1443 self.root_logger.removeHandler(self.root_logger.handlers[0])
1444 self.root_logger.addHandler(self.sl_hdlr)
1445 self.handled = threading.Event()
1446
1447 def tearDown(self):
1448 """Shutdown the UDP server."""
1449 try:
1450 self.server.stop(2.0)
1451 self.root_logger.removeHandler(self.sl_hdlr)
1452 self.sl_hdlr.close()
1453 finally:
1454 BaseTest.tearDown(self)
1455
1456 def handle_datagram(self, request):
1457 self.log_output = request.packet
1458 self.handled.set()
1459
1460 def test_output(self):
1461 # The log message sent to the SysLogHandler is properly received.
1462 logger = logging.getLogger("slh")
1463 logger.error("sp\xe4m")
1464 self.handled.wait()
1465 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1466
1467
1468@unittest.skipUnless(threading, 'Threading required for this test.')
1469class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001470 """Test for HTTPHandler."""
1471
Vinay Sajip9ba87612011-05-21 11:32:15 +01001472 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1473MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1474zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1475aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1476AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
14770tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
14785LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1479piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
14802z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1481i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1482PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1483E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1484rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1485/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1486-----END RSA PRIVATE KEY-----
1487-----BEGIN CERTIFICATE-----
1488MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1489BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1490EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1491xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1492l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1493vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1494BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1495S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1496BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
14974f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
14981c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1499IRbTpvlHWPjsSvHz0ZOH
1500-----END CERTIFICATE-----"""
1501
Vinay Sajip7367d082011-05-02 13:17:27 +01001502 def setUp(self):
1503 """Set up an HTTP server to receive log messages, and a HTTPHandler
1504 pointing to that server's address and port."""
1505 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001506 self.handled = threading.Event()
1507
Vinay Sajip7367d082011-05-02 13:17:27 +01001508 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001509 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001510 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001511 if self.command == 'POST':
1512 try:
1513 rlen = int(request.headers['Content-Length'])
1514 self.post_data = request.rfile.read(rlen)
1515 except:
1516 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001517 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001518 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001519 self.handled.set()
1520
1521 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001522 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001523 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001524 root_logger = self.root_logger
1525 root_logger.removeHandler(self.root_logger.handlers[0])
1526 for secure in (False, True):
1527 addr = ('localhost', 0)
1528 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001529 try:
1530 import ssl
1531 fd, fn = tempfile.mkstemp()
1532 os.close(fd)
1533 with open(fn, 'w') as f:
1534 f.write(self.PEMFILE)
1535 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1536 sslctx.load_cert_chain(fn)
1537 os.unlink(fn)
1538 except ImportError:
1539 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001540 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001541 sslctx = None
1542 self.server = server = TestHTTPServer(addr, self.handle_request,
1543 0.01, sslctx=sslctx)
1544 server.start()
1545 server.ready.wait()
1546 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001547 secure_client = secure and sslctx
1548 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1549 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001550 self.log_data = None
1551 root_logger.addHandler(self.h_hdlr)
1552
1553 for method in ('GET', 'POST'):
1554 self.h_hdlr.method = method
1555 self.handled.clear()
1556 msg = "sp\xe4m"
1557 logger.error(msg)
1558 self.handled.wait()
1559 self.assertEqual(self.log_data.path, '/frob')
1560 self.assertEqual(self.command, method)
1561 if method == 'GET':
1562 d = parse_qs(self.log_data.query)
1563 else:
1564 d = parse_qs(self.post_data.decode('utf-8'))
1565 self.assertEqual(d['name'], ['http'])
1566 self.assertEqual(d['funcName'], ['test_output'])
1567 self.assertEqual(d['msg'], [msg])
1568
1569 self.server.stop(2.0)
1570 self.root_logger.removeHandler(self.h_hdlr)
1571 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001572
Christian Heimes180510d2008-03-03 19:15:45 +00001573class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001574
Christian Heimes180510d2008-03-03 19:15:45 +00001575 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001576
Christian Heimes180510d2008-03-03 19:15:45 +00001577 def setUp(self):
1578 """Create a dict to remember potentially destroyed objects."""
1579 BaseTest.setUp(self)
1580 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001581
Christian Heimes180510d2008-03-03 19:15:45 +00001582 def _watch_for_survival(self, *args):
1583 """Watch the given objects for survival, by creating weakrefs to
1584 them."""
1585 for obj in args:
1586 key = id(obj), repr(obj)
1587 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001588
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001589 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001590 """Assert that all objects watched for survival have survived."""
1591 # Trigger cycle breaking.
1592 gc.collect()
1593 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001594 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001595 if ref() is None:
1596 dead.append(repr_)
1597 if dead:
1598 self.fail("%d objects should have survived "
1599 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001600
Christian Heimes180510d2008-03-03 19:15:45 +00001601 def test_persistent_loggers(self):
1602 # Logger objects are persistent and retain their configuration, even
1603 # if visible references are destroyed.
1604 self.root_logger.setLevel(logging.INFO)
1605 foo = logging.getLogger("foo")
1606 self._watch_for_survival(foo)
1607 foo.setLevel(logging.DEBUG)
1608 self.root_logger.debug(self.next_message())
1609 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001610 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001611 ('foo', 'DEBUG', '2'),
1612 ])
1613 del foo
1614 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001615 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001616 # foo has retained its settings.
1617 bar = logging.getLogger("foo")
1618 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001619 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001620 ('foo', 'DEBUG', '2'),
1621 ('foo', 'DEBUG', '3'),
1622 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001623
Benjamin Petersonf91df042009-02-13 02:50:59 +00001624
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001625class EncodingTest(BaseTest):
1626 def test_encoding_plain_file(self):
1627 # In Python 2.x, a plain file object is treated as having no encoding.
1628 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001629 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1630 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001631 # the non-ascii data we write to the log.
1632 data = "foo\x80"
1633 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001634 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001635 log.addHandler(handler)
1636 try:
1637 # write non-ascii data to the log.
1638 log.warning(data)
1639 finally:
1640 log.removeHandler(handler)
1641 handler.close()
1642 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001643 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001644 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001645 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001646 finally:
1647 f.close()
1648 finally:
1649 if os.path.isfile(fn):
1650 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001651
Benjamin Petersonf91df042009-02-13 02:50:59 +00001652 def test_encoding_cyrillic_unicode(self):
1653 log = logging.getLogger("test")
1654 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1655 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1656 #Ensure it's written in a Cyrillic encoding
1657 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001658 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001659 stream = io.BytesIO()
1660 writer = writer_class(stream, 'strict')
1661 handler = logging.StreamHandler(writer)
1662 log.addHandler(handler)
1663 try:
1664 log.warning(message)
1665 finally:
1666 log.removeHandler(handler)
1667 handler.close()
1668 # check we wrote exactly those bytes, ignoring trailing \n etc
1669 s = stream.getvalue()
1670 #Compare against what the data should be when encoded in CP-1251
1671 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1672
1673
Georg Brandlf9734072008-12-07 15:30:06 +00001674class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001675
Georg Brandlf9734072008-12-07 15:30:06 +00001676 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001677 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001678 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001679 self.addCleanup(lambda: logging.captureWarnings(False))
1680 warnings.filterwarnings("always", category=UserWarning)
1681 stream = io.StringIO()
1682 h = logging.StreamHandler(stream)
1683 logger = logging.getLogger("py.warnings")
1684 logger.addHandler(h)
1685 warnings.warn("I'm warning you...")
1686 logger.removeHandler(h)
1687 s = stream.getvalue()
1688 h.close()
1689 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001690
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001691 #See if an explicit file uses the original implementation
1692 a_file = io.StringIO()
1693 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1694 a_file, "Dummy line")
1695 s = a_file.getvalue()
1696 a_file.close()
1697 self.assertEqual(s,
1698 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1699
1700 def test_warnings_no_handlers(self):
1701 with warnings.catch_warnings():
1702 logging.captureWarnings(True)
1703 self.addCleanup(lambda: logging.captureWarnings(False))
1704
1705 # confirm our assumption: no loggers are set
1706 logger = logging.getLogger("py.warnings")
1707 assert logger.handlers == []
1708
1709 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1710 self.assertTrue(len(logger.handlers) == 1)
1711 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001712
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001713
1714def formatFunc(format, datefmt=None):
1715 return logging.Formatter(format, datefmt)
1716
1717def handlerFunc():
1718 return logging.StreamHandler()
1719
1720class CustomHandler(logging.StreamHandler):
1721 pass
1722
1723class ConfigDictTest(BaseTest):
1724
1725 """Reading logging config from a dictionary."""
1726
1727 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1728
1729 # config0 is a standard configuration.
1730 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001731 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001732 'formatters': {
1733 'form1' : {
1734 'format' : '%(levelname)s ++ %(message)s',
1735 },
1736 },
1737 'handlers' : {
1738 'hand1' : {
1739 'class' : 'logging.StreamHandler',
1740 'formatter' : 'form1',
1741 'level' : 'NOTSET',
1742 'stream' : 'ext://sys.stdout',
1743 },
1744 },
1745 'root' : {
1746 'level' : 'WARNING',
1747 'handlers' : ['hand1'],
1748 },
1749 }
1750
1751 # config1 adds a little to the standard configuration.
1752 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001753 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001754 'formatters': {
1755 'form1' : {
1756 'format' : '%(levelname)s ++ %(message)s',
1757 },
1758 },
1759 'handlers' : {
1760 'hand1' : {
1761 'class' : 'logging.StreamHandler',
1762 'formatter' : 'form1',
1763 'level' : 'NOTSET',
1764 'stream' : 'ext://sys.stdout',
1765 },
1766 },
1767 'loggers' : {
1768 'compiler.parser' : {
1769 'level' : 'DEBUG',
1770 'handlers' : ['hand1'],
1771 },
1772 },
1773 'root' : {
1774 'level' : 'WARNING',
1775 },
1776 }
1777
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001778 # config1a moves the handler to the root. Used with config8a
1779 config1a = {
1780 'version': 1,
1781 'formatters': {
1782 'form1' : {
1783 'format' : '%(levelname)s ++ %(message)s',
1784 },
1785 },
1786 'handlers' : {
1787 'hand1' : {
1788 'class' : 'logging.StreamHandler',
1789 'formatter' : 'form1',
1790 'level' : 'NOTSET',
1791 'stream' : 'ext://sys.stdout',
1792 },
1793 },
1794 'loggers' : {
1795 'compiler.parser' : {
1796 'level' : 'DEBUG',
1797 },
1798 },
1799 'root' : {
1800 'level' : 'WARNING',
1801 'handlers' : ['hand1'],
1802 },
1803 }
1804
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001805 # config2 has a subtle configuration error that should be reported
1806 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001807 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001808 'formatters': {
1809 'form1' : {
1810 'format' : '%(levelname)s ++ %(message)s',
1811 },
1812 },
1813 'handlers' : {
1814 'hand1' : {
1815 'class' : 'logging.StreamHandler',
1816 'formatter' : 'form1',
1817 'level' : 'NOTSET',
1818 'stream' : 'ext://sys.stdbout',
1819 },
1820 },
1821 'loggers' : {
1822 'compiler.parser' : {
1823 'level' : 'DEBUG',
1824 'handlers' : ['hand1'],
1825 },
1826 },
1827 'root' : {
1828 'level' : 'WARNING',
1829 },
1830 }
1831
1832 #As config1 but with a misspelt level on a handler
1833 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001834 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001835 'formatters': {
1836 'form1' : {
1837 'format' : '%(levelname)s ++ %(message)s',
1838 },
1839 },
1840 'handlers' : {
1841 'hand1' : {
1842 'class' : 'logging.StreamHandler',
1843 'formatter' : 'form1',
1844 'level' : 'NTOSET',
1845 'stream' : 'ext://sys.stdout',
1846 },
1847 },
1848 'loggers' : {
1849 'compiler.parser' : {
1850 'level' : 'DEBUG',
1851 'handlers' : ['hand1'],
1852 },
1853 },
1854 'root' : {
1855 'level' : 'WARNING',
1856 },
1857 }
1858
1859
1860 #As config1 but with a misspelt level on a logger
1861 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001862 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001863 'formatters': {
1864 'form1' : {
1865 'format' : '%(levelname)s ++ %(message)s',
1866 },
1867 },
1868 'handlers' : {
1869 'hand1' : {
1870 'class' : 'logging.StreamHandler',
1871 'formatter' : 'form1',
1872 'level' : 'NOTSET',
1873 'stream' : 'ext://sys.stdout',
1874 },
1875 },
1876 'loggers' : {
1877 'compiler.parser' : {
1878 'level' : 'DEBUG',
1879 'handlers' : ['hand1'],
1880 },
1881 },
1882 'root' : {
1883 'level' : 'WRANING',
1884 },
1885 }
1886
1887 # config3 has a less subtle configuration error
1888 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001889 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001890 'formatters': {
1891 'form1' : {
1892 'format' : '%(levelname)s ++ %(message)s',
1893 },
1894 },
1895 'handlers' : {
1896 'hand1' : {
1897 'class' : 'logging.StreamHandler',
1898 'formatter' : 'misspelled_name',
1899 'level' : 'NOTSET',
1900 'stream' : 'ext://sys.stdout',
1901 },
1902 },
1903 'loggers' : {
1904 'compiler.parser' : {
1905 'level' : 'DEBUG',
1906 'handlers' : ['hand1'],
1907 },
1908 },
1909 'root' : {
1910 'level' : 'WARNING',
1911 },
1912 }
1913
1914 # config4 specifies a custom formatter class to be loaded
1915 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001916 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001917 'formatters': {
1918 'form1' : {
1919 '()' : __name__ + '.ExceptionFormatter',
1920 'format' : '%(levelname)s:%(name)s:%(message)s',
1921 },
1922 },
1923 'handlers' : {
1924 'hand1' : {
1925 'class' : 'logging.StreamHandler',
1926 'formatter' : 'form1',
1927 'level' : 'NOTSET',
1928 'stream' : 'ext://sys.stdout',
1929 },
1930 },
1931 'root' : {
1932 'level' : 'NOTSET',
1933 'handlers' : ['hand1'],
1934 },
1935 }
1936
1937 # As config4 but using an actual callable rather than a string
1938 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001939 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001940 'formatters': {
1941 'form1' : {
1942 '()' : ExceptionFormatter,
1943 'format' : '%(levelname)s:%(name)s:%(message)s',
1944 },
1945 'form2' : {
1946 '()' : __name__ + '.formatFunc',
1947 'format' : '%(levelname)s:%(name)s:%(message)s',
1948 },
1949 'form3' : {
1950 '()' : formatFunc,
1951 'format' : '%(levelname)s:%(name)s:%(message)s',
1952 },
1953 },
1954 'handlers' : {
1955 'hand1' : {
1956 'class' : 'logging.StreamHandler',
1957 'formatter' : 'form1',
1958 'level' : 'NOTSET',
1959 'stream' : 'ext://sys.stdout',
1960 },
1961 'hand2' : {
1962 '()' : handlerFunc,
1963 },
1964 },
1965 'root' : {
1966 'level' : 'NOTSET',
1967 'handlers' : ['hand1'],
1968 },
1969 }
1970
1971 # config5 specifies a custom handler class to be loaded
1972 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001973 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001974 'formatters': {
1975 'form1' : {
1976 'format' : '%(levelname)s ++ %(message)s',
1977 },
1978 },
1979 'handlers' : {
1980 'hand1' : {
1981 'class' : __name__ + '.CustomHandler',
1982 'formatter' : 'form1',
1983 'level' : 'NOTSET',
1984 'stream' : 'ext://sys.stdout',
1985 },
1986 },
1987 'loggers' : {
1988 'compiler.parser' : {
1989 'level' : 'DEBUG',
1990 'handlers' : ['hand1'],
1991 },
1992 },
1993 'root' : {
1994 'level' : 'WARNING',
1995 },
1996 }
1997
1998 # config6 specifies a custom handler class to be loaded
1999 # but has bad arguments
2000 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002001 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002002 'formatters': {
2003 'form1' : {
2004 'format' : '%(levelname)s ++ %(message)s',
2005 },
2006 },
2007 'handlers' : {
2008 'hand1' : {
2009 'class' : __name__ + '.CustomHandler',
2010 'formatter' : 'form1',
2011 'level' : 'NOTSET',
2012 'stream' : 'ext://sys.stdout',
2013 '9' : 'invalid parameter name',
2014 },
2015 },
2016 'loggers' : {
2017 'compiler.parser' : {
2018 'level' : 'DEBUG',
2019 'handlers' : ['hand1'],
2020 },
2021 },
2022 'root' : {
2023 'level' : 'WARNING',
2024 },
2025 }
2026
2027 #config 7 does not define compiler.parser but defines compiler.lexer
2028 #so compiler.parser should be disabled after applying it
2029 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002030 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002031 'formatters': {
2032 'form1' : {
2033 'format' : '%(levelname)s ++ %(message)s',
2034 },
2035 },
2036 'handlers' : {
2037 'hand1' : {
2038 'class' : 'logging.StreamHandler',
2039 'formatter' : 'form1',
2040 'level' : 'NOTSET',
2041 'stream' : 'ext://sys.stdout',
2042 },
2043 },
2044 'loggers' : {
2045 'compiler.lexer' : {
2046 'level' : 'DEBUG',
2047 'handlers' : ['hand1'],
2048 },
2049 },
2050 'root' : {
2051 'level' : 'WARNING',
2052 },
2053 }
2054
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002055 # config8 defines both compiler and compiler.lexer
2056 # so compiler.parser should not be disabled (since
2057 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002058 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002059 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002060 'disable_existing_loggers' : False,
2061 'formatters': {
2062 'form1' : {
2063 'format' : '%(levelname)s ++ %(message)s',
2064 },
2065 },
2066 'handlers' : {
2067 'hand1' : {
2068 'class' : 'logging.StreamHandler',
2069 'formatter' : 'form1',
2070 'level' : 'NOTSET',
2071 'stream' : 'ext://sys.stdout',
2072 },
2073 },
2074 'loggers' : {
2075 'compiler' : {
2076 'level' : 'DEBUG',
2077 'handlers' : ['hand1'],
2078 },
2079 'compiler.lexer' : {
2080 },
2081 },
2082 'root' : {
2083 'level' : 'WARNING',
2084 },
2085 }
2086
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002087 # config8a disables existing loggers
2088 config8a = {
2089 'version': 1,
2090 'disable_existing_loggers' : True,
2091 'formatters': {
2092 'form1' : {
2093 'format' : '%(levelname)s ++ %(message)s',
2094 },
2095 },
2096 'handlers' : {
2097 'hand1' : {
2098 'class' : 'logging.StreamHandler',
2099 'formatter' : 'form1',
2100 'level' : 'NOTSET',
2101 'stream' : 'ext://sys.stdout',
2102 },
2103 },
2104 'loggers' : {
2105 'compiler' : {
2106 'level' : 'DEBUG',
2107 'handlers' : ['hand1'],
2108 },
2109 'compiler.lexer' : {
2110 },
2111 },
2112 'root' : {
2113 'level' : 'WARNING',
2114 },
2115 }
2116
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002117 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002118 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002119 'formatters': {
2120 'form1' : {
2121 'format' : '%(levelname)s ++ %(message)s',
2122 },
2123 },
2124 'handlers' : {
2125 'hand1' : {
2126 'class' : 'logging.StreamHandler',
2127 'formatter' : 'form1',
2128 'level' : 'WARNING',
2129 'stream' : 'ext://sys.stdout',
2130 },
2131 },
2132 'loggers' : {
2133 'compiler.parser' : {
2134 'level' : 'WARNING',
2135 'handlers' : ['hand1'],
2136 },
2137 },
2138 'root' : {
2139 'level' : 'NOTSET',
2140 },
2141 }
2142
2143 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002144 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002145 'incremental' : True,
2146 'handlers' : {
2147 'hand1' : {
2148 'level' : 'WARNING',
2149 },
2150 },
2151 'loggers' : {
2152 'compiler.parser' : {
2153 'level' : 'INFO',
2154 },
2155 },
2156 }
2157
2158 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002159 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002160 'incremental' : True,
2161 'handlers' : {
2162 'hand1' : {
2163 'level' : 'INFO',
2164 },
2165 },
2166 'loggers' : {
2167 'compiler.parser' : {
2168 'level' : 'INFO',
2169 },
2170 },
2171 }
2172
2173 #As config1 but with a filter added
2174 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002175 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002176 'formatters': {
2177 'form1' : {
2178 'format' : '%(levelname)s ++ %(message)s',
2179 },
2180 },
2181 'filters' : {
2182 'filt1' : {
2183 'name' : 'compiler.parser',
2184 },
2185 },
2186 'handlers' : {
2187 'hand1' : {
2188 'class' : 'logging.StreamHandler',
2189 'formatter' : 'form1',
2190 'level' : 'NOTSET',
2191 'stream' : 'ext://sys.stdout',
2192 'filters' : ['filt1'],
2193 },
2194 },
2195 'loggers' : {
2196 'compiler.parser' : {
2197 'level' : 'DEBUG',
2198 'filters' : ['filt1'],
2199 },
2200 },
2201 'root' : {
2202 'level' : 'WARNING',
2203 'handlers' : ['hand1'],
2204 },
2205 }
2206
2207 #As config1 but using cfg:// references
2208 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002209 'version': 1,
2210 'true_formatters': {
2211 'form1' : {
2212 'format' : '%(levelname)s ++ %(message)s',
2213 },
2214 },
2215 'handler_configs': {
2216 'hand1' : {
2217 'class' : 'logging.StreamHandler',
2218 'formatter' : 'form1',
2219 'level' : 'NOTSET',
2220 'stream' : 'ext://sys.stdout',
2221 },
2222 },
2223 'formatters' : 'cfg://true_formatters',
2224 'handlers' : {
2225 'hand1' : 'cfg://handler_configs[hand1]',
2226 },
2227 'loggers' : {
2228 'compiler.parser' : {
2229 'level' : 'DEBUG',
2230 'handlers' : ['hand1'],
2231 },
2232 },
2233 'root' : {
2234 'level' : 'WARNING',
2235 },
2236 }
2237
2238 #As config11 but missing the version key
2239 config12 = {
2240 'true_formatters': {
2241 'form1' : {
2242 'format' : '%(levelname)s ++ %(message)s',
2243 },
2244 },
2245 'handler_configs': {
2246 'hand1' : {
2247 'class' : 'logging.StreamHandler',
2248 'formatter' : 'form1',
2249 'level' : 'NOTSET',
2250 'stream' : 'ext://sys.stdout',
2251 },
2252 },
2253 'formatters' : 'cfg://true_formatters',
2254 'handlers' : {
2255 'hand1' : 'cfg://handler_configs[hand1]',
2256 },
2257 'loggers' : {
2258 'compiler.parser' : {
2259 'level' : 'DEBUG',
2260 'handlers' : ['hand1'],
2261 },
2262 },
2263 'root' : {
2264 'level' : 'WARNING',
2265 },
2266 }
2267
2268 #As config11 but using an unsupported version
2269 config13 = {
2270 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002271 'true_formatters': {
2272 'form1' : {
2273 'format' : '%(levelname)s ++ %(message)s',
2274 },
2275 },
2276 'handler_configs': {
2277 'hand1' : {
2278 'class' : 'logging.StreamHandler',
2279 'formatter' : 'form1',
2280 'level' : 'NOTSET',
2281 'stream' : 'ext://sys.stdout',
2282 },
2283 },
2284 'formatters' : 'cfg://true_formatters',
2285 'handlers' : {
2286 'hand1' : 'cfg://handler_configs[hand1]',
2287 },
2288 'loggers' : {
2289 'compiler.parser' : {
2290 'level' : 'DEBUG',
2291 'handlers' : ['hand1'],
2292 },
2293 },
2294 'root' : {
2295 'level' : 'WARNING',
2296 },
2297 }
2298
2299 def apply_config(self, conf):
2300 logging.config.dictConfig(conf)
2301
2302 def test_config0_ok(self):
2303 # A simple config which overrides the default settings.
2304 with captured_stdout() as output:
2305 self.apply_config(self.config0)
2306 logger = logging.getLogger()
2307 # Won't output anything
2308 logger.info(self.next_message())
2309 # Outputs a message
2310 logger.error(self.next_message())
2311 self.assert_log_lines([
2312 ('ERROR', '2'),
2313 ], stream=output)
2314 # Original logger output is empty.
2315 self.assert_log_lines([])
2316
2317 def test_config1_ok(self, config=config1):
2318 # A config defining a sub-parser as well.
2319 with captured_stdout() as output:
2320 self.apply_config(config)
2321 logger = logging.getLogger("compiler.parser")
2322 # Both will output a message
2323 logger.info(self.next_message())
2324 logger.error(self.next_message())
2325 self.assert_log_lines([
2326 ('INFO', '1'),
2327 ('ERROR', '2'),
2328 ], stream=output)
2329 # Original logger output is empty.
2330 self.assert_log_lines([])
2331
2332 def test_config2_failure(self):
2333 # A simple config which overrides the default settings.
2334 self.assertRaises(Exception, self.apply_config, self.config2)
2335
2336 def test_config2a_failure(self):
2337 # A simple config which overrides the default settings.
2338 self.assertRaises(Exception, self.apply_config, self.config2a)
2339
2340 def test_config2b_failure(self):
2341 # A simple config which overrides the default settings.
2342 self.assertRaises(Exception, self.apply_config, self.config2b)
2343
2344 def test_config3_failure(self):
2345 # A simple config which overrides the default settings.
2346 self.assertRaises(Exception, self.apply_config, self.config3)
2347
2348 def test_config4_ok(self):
2349 # A config specifying a custom formatter class.
2350 with captured_stdout() as output:
2351 self.apply_config(self.config4)
2352 #logger = logging.getLogger()
2353 try:
2354 raise RuntimeError()
2355 except RuntimeError:
2356 logging.exception("just testing")
2357 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002358 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002359 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2360 # Original logger output is empty
2361 self.assert_log_lines([])
2362
2363 def test_config4a_ok(self):
2364 # A config specifying a custom formatter class.
2365 with captured_stdout() as output:
2366 self.apply_config(self.config4a)
2367 #logger = logging.getLogger()
2368 try:
2369 raise RuntimeError()
2370 except RuntimeError:
2371 logging.exception("just testing")
2372 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002373 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002374 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2375 # Original logger output is empty
2376 self.assert_log_lines([])
2377
2378 def test_config5_ok(self):
2379 self.test_config1_ok(config=self.config5)
2380
2381 def test_config6_failure(self):
2382 self.assertRaises(Exception, self.apply_config, self.config6)
2383
2384 def test_config7_ok(self):
2385 with captured_stdout() as output:
2386 self.apply_config(self.config1)
2387 logger = logging.getLogger("compiler.parser")
2388 # Both will output a message
2389 logger.info(self.next_message())
2390 logger.error(self.next_message())
2391 self.assert_log_lines([
2392 ('INFO', '1'),
2393 ('ERROR', '2'),
2394 ], stream=output)
2395 # Original logger output is empty.
2396 self.assert_log_lines([])
2397 with captured_stdout() as output:
2398 self.apply_config(self.config7)
2399 logger = logging.getLogger("compiler.parser")
2400 self.assertTrue(logger.disabled)
2401 logger = logging.getLogger("compiler.lexer")
2402 # Both will output a message
2403 logger.info(self.next_message())
2404 logger.error(self.next_message())
2405 self.assert_log_lines([
2406 ('INFO', '3'),
2407 ('ERROR', '4'),
2408 ], stream=output)
2409 # Original logger output is empty.
2410 self.assert_log_lines([])
2411
2412 #Same as test_config_7_ok but don't disable old loggers.
2413 def test_config_8_ok(self):
2414 with captured_stdout() as output:
2415 self.apply_config(self.config1)
2416 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002417 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002418 logger.info(self.next_message())
2419 logger.error(self.next_message())
2420 self.assert_log_lines([
2421 ('INFO', '1'),
2422 ('ERROR', '2'),
2423 ], stream=output)
2424 # Original logger output is empty.
2425 self.assert_log_lines([])
2426 with captured_stdout() as output:
2427 self.apply_config(self.config8)
2428 logger = logging.getLogger("compiler.parser")
2429 self.assertFalse(logger.disabled)
2430 # Both will output a message
2431 logger.info(self.next_message())
2432 logger.error(self.next_message())
2433 logger = logging.getLogger("compiler.lexer")
2434 # Both will output a message
2435 logger.info(self.next_message())
2436 logger.error(self.next_message())
2437 self.assert_log_lines([
2438 ('INFO', '3'),
2439 ('ERROR', '4'),
2440 ('INFO', '5'),
2441 ('ERROR', '6'),
2442 ], stream=output)
2443 # Original logger output is empty.
2444 self.assert_log_lines([])
2445
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002446 def test_config_8a_ok(self):
2447 with captured_stdout() as output:
2448 self.apply_config(self.config1a)
2449 logger = logging.getLogger("compiler.parser")
2450 # See issue #11424. compiler-hyphenated sorts
2451 # between compiler and compiler.xyz and this
2452 # was preventing compiler.xyz from being included
2453 # in the child loggers of compiler because of an
2454 # overzealous loop termination condition.
2455 hyphenated = logging.getLogger('compiler-hyphenated')
2456 # All will output a message
2457 logger.info(self.next_message())
2458 logger.error(self.next_message())
2459 hyphenated.critical(self.next_message())
2460 self.assert_log_lines([
2461 ('INFO', '1'),
2462 ('ERROR', '2'),
2463 ('CRITICAL', '3'),
2464 ], stream=output)
2465 # Original logger output is empty.
2466 self.assert_log_lines([])
2467 with captured_stdout() as output:
2468 self.apply_config(self.config8a)
2469 logger = logging.getLogger("compiler.parser")
2470 self.assertFalse(logger.disabled)
2471 # Both will output a message
2472 logger.info(self.next_message())
2473 logger.error(self.next_message())
2474 logger = logging.getLogger("compiler.lexer")
2475 # Both will output a message
2476 logger.info(self.next_message())
2477 logger.error(self.next_message())
2478 # Will not appear
2479 hyphenated.critical(self.next_message())
2480 self.assert_log_lines([
2481 ('INFO', '4'),
2482 ('ERROR', '5'),
2483 ('INFO', '6'),
2484 ('ERROR', '7'),
2485 ], stream=output)
2486 # Original logger output is empty.
2487 self.assert_log_lines([])
2488
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002489 def test_config_9_ok(self):
2490 with captured_stdout() as output:
2491 self.apply_config(self.config9)
2492 logger = logging.getLogger("compiler.parser")
2493 #Nothing will be output since both handler and logger are set to WARNING
2494 logger.info(self.next_message())
2495 self.assert_log_lines([], stream=output)
2496 self.apply_config(self.config9a)
2497 #Nothing will be output since both handler is still set to WARNING
2498 logger.info(self.next_message())
2499 self.assert_log_lines([], stream=output)
2500 self.apply_config(self.config9b)
2501 #Message should now be output
2502 logger.info(self.next_message())
2503 self.assert_log_lines([
2504 ('INFO', '3'),
2505 ], stream=output)
2506
2507 def test_config_10_ok(self):
2508 with captured_stdout() as output:
2509 self.apply_config(self.config10)
2510 logger = logging.getLogger("compiler.parser")
2511 logger.warning(self.next_message())
2512 logger = logging.getLogger('compiler')
2513 #Not output, because filtered
2514 logger.warning(self.next_message())
2515 logger = logging.getLogger('compiler.lexer')
2516 #Not output, because filtered
2517 logger.warning(self.next_message())
2518 logger = logging.getLogger("compiler.parser.codegen")
2519 #Output, as not filtered
2520 logger.error(self.next_message())
2521 self.assert_log_lines([
2522 ('WARNING', '1'),
2523 ('ERROR', '4'),
2524 ], stream=output)
2525
2526 def test_config11_ok(self):
2527 self.test_config1_ok(self.config11)
2528
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002529 def test_config12_failure(self):
2530 self.assertRaises(Exception, self.apply_config, self.config12)
2531
2532 def test_config13_failure(self):
2533 self.assertRaises(Exception, self.apply_config, self.config13)
2534
Victor Stinner45df8202010-04-28 22:31:17 +00002535 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002536 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002537 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002538 # Ask for a randomly assigned port (by using port 0)
2539 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002540 t.start()
2541 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002542 # Now get the port allocated
2543 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002544 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002545 try:
2546 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2547 sock.settimeout(2.0)
2548 sock.connect(('localhost', port))
2549
2550 slen = struct.pack('>L', len(text))
2551 s = slen + text
2552 sentsofar = 0
2553 left = len(s)
2554 while left > 0:
2555 sent = sock.send(s[sentsofar:])
2556 sentsofar += sent
2557 left -= sent
2558 sock.close()
2559 finally:
2560 t.ready.wait(2.0)
2561 logging.config.stopListening()
2562 t.join(2.0)
2563
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002564 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002565 def test_listen_config_10_ok(self):
2566 with captured_stdout() as output:
2567 self.setup_via_listener(json.dumps(self.config10))
2568 logger = logging.getLogger("compiler.parser")
2569 logger.warning(self.next_message())
2570 logger = logging.getLogger('compiler')
2571 #Not output, because filtered
2572 logger.warning(self.next_message())
2573 logger = logging.getLogger('compiler.lexer')
2574 #Not output, because filtered
2575 logger.warning(self.next_message())
2576 logger = logging.getLogger("compiler.parser.codegen")
2577 #Output, as not filtered
2578 logger.error(self.next_message())
2579 self.assert_log_lines([
2580 ('WARNING', '1'),
2581 ('ERROR', '4'),
2582 ], stream=output)
2583
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002584 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002585 def test_listen_config_1_ok(self):
2586 with captured_stdout() as output:
2587 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2588 logger = logging.getLogger("compiler.parser")
2589 # Both will output a message
2590 logger.info(self.next_message())
2591 logger.error(self.next_message())
2592 self.assert_log_lines([
2593 ('INFO', '1'),
2594 ('ERROR', '2'),
2595 ], stream=output)
2596 # Original logger output is empty.
2597 self.assert_log_lines([])
2598
Vinay Sajip373baef2011-04-26 20:05:24 +01002599 def test_baseconfig(self):
2600 d = {
2601 'atuple': (1, 2, 3),
2602 'alist': ['a', 'b', 'c'],
2603 'adict': {'d': 'e', 'f': 3 },
2604 'nest1': ('g', ('h', 'i'), 'j'),
2605 'nest2': ['k', ['l', 'm'], 'n'],
2606 'nest3': ['o', 'cfg://alist', 'p'],
2607 }
2608 bc = logging.config.BaseConfigurator(d)
2609 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2610 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2611 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2612 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2613 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2614 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2615 v = bc.convert('cfg://nest3')
2616 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2617 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2618 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2619 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002620
2621class ManagerTest(BaseTest):
2622 def test_manager_loggerclass(self):
2623 logged = []
2624
2625 class MyLogger(logging.Logger):
2626 def _log(self, level, msg, args, exc_info=None, extra=None):
2627 logged.append(msg)
2628
2629 man = logging.Manager(None)
2630 self.assertRaises(TypeError, man.setLoggerClass, int)
2631 man.setLoggerClass(MyLogger)
2632 logger = man.getLogger('test')
2633 logger.warning('should appear in logged')
2634 logging.warning('should not appear in logged')
2635
2636 self.assertEqual(logged, ['should appear in logged'])
2637
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002638 def test_set_log_record_factory(self):
2639 man = logging.Manager(None)
2640 expected = object()
2641 man.setLogRecordFactory(expected)
2642 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002643
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002644class ChildLoggerTest(BaseTest):
2645 def test_child_loggers(self):
2646 r = logging.getLogger()
2647 l1 = logging.getLogger('abc')
2648 l2 = logging.getLogger('def.ghi')
2649 c1 = r.getChild('xyz')
2650 c2 = r.getChild('uvw.xyz')
2651 self.assertTrue(c1 is logging.getLogger('xyz'))
2652 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2653 c1 = l1.getChild('def')
2654 c2 = c1.getChild('ghi')
2655 c3 = l1.getChild('def.ghi')
2656 self.assertTrue(c1 is logging.getLogger('abc.def'))
2657 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2658 self.assertTrue(c2 is c3)
2659
2660
Vinay Sajip6fac8172010-10-19 20:44:14 +00002661class DerivedLogRecord(logging.LogRecord):
2662 pass
2663
Vinay Sajip61561522010-12-03 11:50:38 +00002664class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002665
2666 def setUp(self):
2667 class CheckingFilter(logging.Filter):
2668 def __init__(self, cls):
2669 self.cls = cls
2670
2671 def filter(self, record):
2672 t = type(record)
2673 if t is not self.cls:
2674 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2675 self.cls)
2676 raise TypeError(msg)
2677 return True
2678
2679 BaseTest.setUp(self)
2680 self.filter = CheckingFilter(DerivedLogRecord)
2681 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002682 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002683
2684 def tearDown(self):
2685 self.root_logger.removeFilter(self.filter)
2686 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002687 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002688
2689 def test_logrecord_class(self):
2690 self.assertRaises(TypeError, self.root_logger.warning,
2691 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002692 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002693 self.root_logger.error(self.next_message())
2694 self.assert_log_lines([
2695 ('root', 'ERROR', '2'),
2696 ])
2697
2698
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002699class QueueHandlerTest(BaseTest):
2700 # Do not bother with a logger name group.
2701 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2702
2703 def setUp(self):
2704 BaseTest.setUp(self)
2705 self.queue = queue.Queue(-1)
2706 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2707 self.que_logger = logging.getLogger('que')
2708 self.que_logger.propagate = False
2709 self.que_logger.setLevel(logging.WARNING)
2710 self.que_logger.addHandler(self.que_hdlr)
2711
2712 def tearDown(self):
2713 self.que_hdlr.close()
2714 BaseTest.tearDown(self)
2715
2716 def test_queue_handler(self):
2717 self.que_logger.debug(self.next_message())
2718 self.assertRaises(queue.Empty, self.queue.get_nowait)
2719 self.que_logger.info(self.next_message())
2720 self.assertRaises(queue.Empty, self.queue.get_nowait)
2721 msg = self.next_message()
2722 self.que_logger.warning(msg)
2723 data = self.queue.get_nowait()
2724 self.assertTrue(isinstance(data, logging.LogRecord))
2725 self.assertEqual(data.name, self.que_logger.name)
2726 self.assertEqual((data.msg, data.args), (msg, None))
2727
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002728 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2729 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002730 def test_queue_listener(self):
2731 handler = TestHandler(Matcher())
2732 listener = logging.handlers.QueueListener(self.queue, handler)
2733 listener.start()
2734 try:
2735 self.que_logger.warning(self.next_message())
2736 self.que_logger.error(self.next_message())
2737 self.que_logger.critical(self.next_message())
2738 finally:
2739 listener.stop()
2740 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2741 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2742 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2743
Vinay Sajip37eb3382011-04-26 20:26:41 +01002744ZERO = datetime.timedelta(0)
2745
2746class UTC(datetime.tzinfo):
2747 def utcoffset(self, dt):
2748 return ZERO
2749
2750 dst = utcoffset
2751
2752 def tzname(self, dt):
2753 return 'UTC'
2754
2755utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002756
Vinay Sajipa39c5712010-10-25 13:57:39 +00002757class FormatterTest(unittest.TestCase):
2758 def setUp(self):
2759 self.common = {
2760 'name': 'formatter.test',
2761 'level': logging.DEBUG,
2762 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2763 'lineno': 42,
2764 'exc_info': None,
2765 'func': None,
2766 'msg': 'Message with %d %s',
2767 'args': (2, 'placeholders'),
2768 }
2769 self.variants = {
2770 }
2771
2772 def get_record(self, name=None):
2773 result = dict(self.common)
2774 if name is not None:
2775 result.update(self.variants[name])
2776 return logging.makeLogRecord(result)
2777
2778 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002779 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002780 r = self.get_record()
2781 f = logging.Formatter('${%(message)s}')
2782 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2783 f = logging.Formatter('%(random)s')
2784 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002785 self.assertFalse(f.usesTime())
2786 f = logging.Formatter('%(asctime)s')
2787 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002788 f = logging.Formatter('%(asctime)-15s')
2789 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002790 f = logging.Formatter('asctime')
2791 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002792
2793 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002794 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002795 r = self.get_record()
2796 f = logging.Formatter('$%{message}%$', style='{')
2797 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2798 f = logging.Formatter('{random}', style='{')
2799 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002800 self.assertFalse(f.usesTime())
2801 f = logging.Formatter('{asctime}', style='{')
2802 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002803 f = logging.Formatter('{asctime!s:15}', style='{')
2804 self.assertTrue(f.usesTime())
2805 f = logging.Formatter('{asctime:15}', style='{')
2806 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002807 f = logging.Formatter('asctime', style='{')
2808 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002809
2810 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002811 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002812 r = self.get_record()
2813 f = logging.Formatter('$message', style='$')
2814 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2815 f = logging.Formatter('$$%${message}%$$', style='$')
2816 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2817 f = logging.Formatter('${random}', style='$')
2818 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002819 self.assertFalse(f.usesTime())
2820 f = logging.Formatter('${asctime}', style='$')
2821 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002822 f = logging.Formatter('${asctime', style='$')
2823 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002824 f = logging.Formatter('$asctime', style='$')
2825 self.assertTrue(f.usesTime())
2826 f = logging.Formatter('asctime', style='$')
2827 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002828
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002829 def test_invalid_style(self):
2830 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2831
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002832 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002833 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002834 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002835 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002836 r.msecs = 123
2837 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002838 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002839 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2840 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002841 f.format(r)
2842 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2843
2844class TestBufferingFormatter(logging.BufferingFormatter):
2845 def formatHeader(self, records):
2846 return '[(%d)' % len(records)
2847
2848 def formatFooter(self, records):
2849 return '(%d)]' % len(records)
2850
2851class BufferingFormatterTest(unittest.TestCase):
2852 def setUp(self):
2853 self.records = [
2854 logging.makeLogRecord({'msg': 'one'}),
2855 logging.makeLogRecord({'msg': 'two'}),
2856 ]
2857
2858 def test_default(self):
2859 f = logging.BufferingFormatter()
2860 self.assertEqual('', f.format([]))
2861 self.assertEqual('onetwo', f.format(self.records))
2862
2863 def test_custom(self):
2864 f = TestBufferingFormatter()
2865 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2866 lf = logging.Formatter('<%(message)s>')
2867 f = TestBufferingFormatter(lf)
2868 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002869
2870class ExceptionTest(BaseTest):
2871 def test_formatting(self):
2872 r = self.root_logger
2873 h = RecordingHandler()
2874 r.addHandler(h)
2875 try:
2876 raise RuntimeError('deliberate mistake')
2877 except:
2878 logging.exception('failed', stack_info=True)
2879 r.removeHandler(h)
2880 h.close()
2881 r = h.records[0]
2882 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2883 'call last):\n'))
2884 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2885 'deliberate mistake'))
2886 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2887 'call last):\n'))
2888 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2889 'stack_info=True)'))
2890
2891
Vinay Sajip5a27d402010-12-10 11:42:57 +00002892class LastResortTest(BaseTest):
2893 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002894 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002895 root = self.root_logger
2896 root.removeHandler(self.root_hdlr)
2897 old_stderr = sys.stderr
2898 old_lastresort = logging.lastResort
2899 old_raise_exceptions = logging.raiseExceptions
2900 try:
2901 sys.stderr = sio = io.StringIO()
2902 root.warning('This is your final chance!')
2903 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2904 #No handlers and no last resort, so 'No handlers' message
2905 logging.lastResort = None
2906 sys.stderr = sio = io.StringIO()
2907 root.warning('This is your final chance!')
2908 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2909 # 'No handlers' message only printed once
2910 sys.stderr = sio = io.StringIO()
2911 root.warning('This is your final chance!')
2912 self.assertEqual(sio.getvalue(), '')
2913 root.manager.emittedNoHandlerWarning = False
2914 #If raiseExceptions is False, no message is printed
2915 logging.raiseExceptions = False
2916 sys.stderr = sio = io.StringIO()
2917 root.warning('This is your final chance!')
2918 self.assertEqual(sio.getvalue(), '')
2919 finally:
2920 sys.stderr = old_stderr
2921 root.addHandler(self.root_hdlr)
2922 logging.lastResort = old_lastresort
2923 logging.raiseExceptions = old_raise_exceptions
2924
2925
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002926class FakeHandler:
2927
2928 def __init__(self, identifier, called):
2929 for method in ('acquire', 'flush', 'close', 'release'):
2930 setattr(self, method, self.record_call(identifier, method, called))
2931
2932 def record_call(self, identifier, method_name, called):
2933 def inner():
2934 called.append('{} - {}'.format(identifier, method_name))
2935 return inner
2936
2937
2938class RecordingHandler(logging.NullHandler):
2939
2940 def __init__(self, *args, **kwargs):
2941 super(RecordingHandler, self).__init__(*args, **kwargs)
2942 self.records = []
2943
2944 def handle(self, record):
2945 """Keep track of all the emitted records."""
2946 self.records.append(record)
2947
2948
2949class ShutdownTest(BaseTest):
2950
Vinay Sajip5e66b162011-04-20 15:41:14 +01002951 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002952
2953 def setUp(self):
2954 super(ShutdownTest, self).setUp()
2955 self.called = []
2956
2957 raise_exceptions = logging.raiseExceptions
2958 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2959
2960 def raise_error(self, error):
2961 def inner():
2962 raise error()
2963 return inner
2964
2965 def test_no_failure(self):
2966 # create some fake handlers
2967 handler0 = FakeHandler(0, self.called)
2968 handler1 = FakeHandler(1, self.called)
2969 handler2 = FakeHandler(2, self.called)
2970
2971 # create live weakref to those handlers
2972 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2973
2974 logging.shutdown(handlerList=list(handlers))
2975
2976 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2977 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2978 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2979 self.assertEqual(expected, self.called)
2980
2981 def _test_with_failure_in_method(self, method, error):
2982 handler = FakeHandler(0, self.called)
2983 setattr(handler, method, self.raise_error(error))
2984 handlers = [logging.weakref.ref(handler)]
2985
2986 logging.shutdown(handlerList=list(handlers))
2987
2988 self.assertEqual('0 - release', self.called[-1])
2989
2990 def test_with_ioerror_in_acquire(self):
2991 self._test_with_failure_in_method('acquire', IOError)
2992
2993 def test_with_ioerror_in_flush(self):
2994 self._test_with_failure_in_method('flush', IOError)
2995
2996 def test_with_ioerror_in_close(self):
2997 self._test_with_failure_in_method('close', IOError)
2998
2999 def test_with_valueerror_in_acquire(self):
3000 self._test_with_failure_in_method('acquire', ValueError)
3001
3002 def test_with_valueerror_in_flush(self):
3003 self._test_with_failure_in_method('flush', ValueError)
3004
3005 def test_with_valueerror_in_close(self):
3006 self._test_with_failure_in_method('close', ValueError)
3007
3008 def test_with_other_error_in_acquire_without_raise(self):
3009 logging.raiseExceptions = False
3010 self._test_with_failure_in_method('acquire', IndexError)
3011
3012 def test_with_other_error_in_flush_without_raise(self):
3013 logging.raiseExceptions = False
3014 self._test_with_failure_in_method('flush', IndexError)
3015
3016 def test_with_other_error_in_close_without_raise(self):
3017 logging.raiseExceptions = False
3018 self._test_with_failure_in_method('close', IndexError)
3019
3020 def test_with_other_error_in_acquire_with_raise(self):
3021 logging.raiseExceptions = True
3022 self.assertRaises(IndexError, self._test_with_failure_in_method,
3023 'acquire', IndexError)
3024
3025 def test_with_other_error_in_flush_with_raise(self):
3026 logging.raiseExceptions = True
3027 self.assertRaises(IndexError, self._test_with_failure_in_method,
3028 'flush', IndexError)
3029
3030 def test_with_other_error_in_close_with_raise(self):
3031 logging.raiseExceptions = True
3032 self.assertRaises(IndexError, self._test_with_failure_in_method,
3033 'close', IndexError)
3034
3035
3036class ModuleLevelMiscTest(BaseTest):
3037
Vinay Sajip5e66b162011-04-20 15:41:14 +01003038 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003039
3040 def test_disable(self):
3041 old_disable = logging.root.manager.disable
3042 # confirm our assumptions are correct
3043 assert old_disable == 0
3044 self.addCleanup(lambda: logging.disable(old_disable))
3045
3046 logging.disable(83)
3047 self.assertEqual(logging.root.manager.disable, 83)
3048
3049 def _test_log(self, method, level=None):
3050 called = []
3051 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003052 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003053
3054 recording = RecordingHandler()
3055 logging.root.addHandler(recording)
3056
3057 log_method = getattr(logging, method)
3058 if level is not None:
3059 log_method(level, "test me: %r", recording)
3060 else:
3061 log_method("test me: %r", recording)
3062
3063 self.assertEqual(len(recording.records), 1)
3064 record = recording.records[0]
3065 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3066
3067 expected_level = level if level is not None else getattr(logging, method.upper())
3068 self.assertEqual(record.levelno, expected_level)
3069
3070 # basicConfig was not called!
3071 self.assertEqual(called, [])
3072
3073 def test_log(self):
3074 self._test_log('log', logging.ERROR)
3075
3076 def test_debug(self):
3077 self._test_log('debug')
3078
3079 def test_info(self):
3080 self._test_log('info')
3081
3082 def test_warning(self):
3083 self._test_log('warning')
3084
3085 def test_error(self):
3086 self._test_log('error')
3087
3088 def test_critical(self):
3089 self._test_log('critical')
3090
3091 def test_set_logger_class(self):
3092 self.assertRaises(TypeError, logging.setLoggerClass, object)
3093
3094 class MyLogger(logging.Logger):
3095 pass
3096
3097 logging.setLoggerClass(MyLogger)
3098 self.assertEqual(logging.getLoggerClass(), MyLogger)
3099
3100 logging.setLoggerClass(logging.Logger)
3101 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3102
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003103class LogRecordTest(BaseTest):
3104 def test_str_rep(self):
3105 r = logging.makeLogRecord({})
3106 s = str(r)
3107 self.assertTrue(s.startswith('<LogRecord: '))
3108 self.assertTrue(s.endswith('>'))
3109
3110 def test_dict_arg(self):
3111 h = RecordingHandler()
3112 r = logging.getLogger()
3113 r.addHandler(h)
3114 d = {'less' : 'more' }
3115 logging.warning('less is %(less)s', d)
3116 self.assertIs(h.records[0].args, d)
3117 self.assertEqual(h.records[0].message, 'less is more')
3118 r.removeHandler(h)
3119 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003120
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003121 def test_multiprocessing(self):
3122 r = logging.makeLogRecord({})
3123 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003124 try:
3125 import multiprocessing as mp
3126 r = logging.makeLogRecord({})
3127 self.assertEqual(r.processName, mp.current_process().name)
3128 except ImportError:
3129 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003130
3131 def test_optional(self):
3132 r = logging.makeLogRecord({})
3133 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003134 if threading:
3135 NOT_NONE(r.thread)
3136 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003137 NOT_NONE(r.process)
3138 NOT_NONE(r.processName)
3139 log_threads = logging.logThreads
3140 log_processes = logging.logProcesses
3141 log_multiprocessing = logging.logMultiprocessing
3142 try:
3143 logging.logThreads = False
3144 logging.logProcesses = False
3145 logging.logMultiprocessing = False
3146 r = logging.makeLogRecord({})
3147 NONE = self.assertIsNone
3148 NONE(r.thread)
3149 NONE(r.threadName)
3150 NONE(r.process)
3151 NONE(r.processName)
3152 finally:
3153 logging.logThreads = log_threads
3154 logging.logProcesses = log_processes
3155 logging.logMultiprocessing = log_multiprocessing
3156
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003157class BasicConfigTest(unittest.TestCase):
3158
Vinay Sajip95bf5042011-04-20 11:50:56 +01003159 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003160
3161 def setUp(self):
3162 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003163 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003164 self.saved_handlers = logging._handlers.copy()
3165 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003166 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003167 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003168 logging.root.handlers = []
3169
3170 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003171 for h in logging.root.handlers[:]:
3172 logging.root.removeHandler(h)
3173 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003174 super(BasicConfigTest, self).tearDown()
3175
Vinay Sajip3def7e02011-04-20 10:58:06 +01003176 def cleanup(self):
3177 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003178 logging._handlers.clear()
3179 logging._handlers.update(self.saved_handlers)
3180 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003181 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003182
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003183 def test_no_kwargs(self):
3184 logging.basicConfig()
3185
3186 # handler defaults to a StreamHandler to sys.stderr
3187 self.assertEqual(len(logging.root.handlers), 1)
3188 handler = logging.root.handlers[0]
3189 self.assertIsInstance(handler, logging.StreamHandler)
3190 self.assertEqual(handler.stream, sys.stderr)
3191
3192 formatter = handler.formatter
3193 # format defaults to logging.BASIC_FORMAT
3194 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3195 # datefmt defaults to None
3196 self.assertIsNone(formatter.datefmt)
3197 # style defaults to %
3198 self.assertIsInstance(formatter._style, logging.PercentStyle)
3199
3200 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003201 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003202
3203 def test_filename(self):
3204 logging.basicConfig(filename='test.log')
3205
3206 self.assertEqual(len(logging.root.handlers), 1)
3207 handler = logging.root.handlers[0]
3208 self.assertIsInstance(handler, logging.FileHandler)
3209
3210 expected = logging.FileHandler('test.log', 'a')
3211 self.addCleanup(expected.close)
3212 self.assertEqual(handler.stream.mode, expected.stream.mode)
3213 self.assertEqual(handler.stream.name, expected.stream.name)
3214
3215 def test_filemode(self):
3216 logging.basicConfig(filename='test.log', filemode='wb')
3217
3218 handler = logging.root.handlers[0]
3219 expected = logging.FileHandler('test.log', 'wb')
3220 self.addCleanup(expected.close)
3221 self.assertEqual(handler.stream.mode, expected.stream.mode)
3222
3223 def test_stream(self):
3224 stream = io.StringIO()
3225 self.addCleanup(stream.close)
3226 logging.basicConfig(stream=stream)
3227
3228 self.assertEqual(len(logging.root.handlers), 1)
3229 handler = logging.root.handlers[0]
3230 self.assertIsInstance(handler, logging.StreamHandler)
3231 self.assertEqual(handler.stream, stream)
3232
3233 def test_format(self):
3234 logging.basicConfig(format='foo')
3235
3236 formatter = logging.root.handlers[0].formatter
3237 self.assertEqual(formatter._style._fmt, 'foo')
3238
3239 def test_datefmt(self):
3240 logging.basicConfig(datefmt='bar')
3241
3242 formatter = logging.root.handlers[0].formatter
3243 self.assertEqual(formatter.datefmt, 'bar')
3244
3245 def test_style(self):
3246 logging.basicConfig(style='$')
3247
3248 formatter = logging.root.handlers[0].formatter
3249 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3250
3251 def test_level(self):
3252 old_level = logging.root.level
3253 self.addCleanup(lambda: logging.root.setLevel(old_level))
3254
3255 logging.basicConfig(level=57)
3256 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003257 # Test that second call has no effect
3258 logging.basicConfig(level=58)
3259 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003260
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003261 def test_incompatible(self):
3262 assertRaises = self.assertRaises
3263 handlers = [logging.StreamHandler()]
3264 stream = sys.stderr
3265 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3266 stream=stream)
3267 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3268 handlers=handlers)
3269 assertRaises(ValueError, logging.basicConfig, stream=stream,
3270 handlers=handlers)
3271
3272 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003273 handlers = [
3274 logging.StreamHandler(),
3275 logging.StreamHandler(sys.stdout),
3276 logging.StreamHandler(),
3277 ]
3278 f = logging.Formatter()
3279 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003280 logging.basicConfig(handlers=handlers)
3281 self.assertIs(handlers[0], logging.root.handlers[0])
3282 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003283 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003284 self.assertIsNotNone(handlers[0].formatter)
3285 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003286 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003287 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3288
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003289 def _test_log(self, method, level=None):
3290 # logging.root has no handlers so basicConfig should be called
3291 called = []
3292
3293 old_basic_config = logging.basicConfig
3294 def my_basic_config(*a, **kw):
3295 old_basic_config()
3296 old_level = logging.root.level
3297 logging.root.setLevel(100) # avoid having messages in stderr
3298 self.addCleanup(lambda: logging.root.setLevel(old_level))
3299 called.append((a, kw))
3300
3301 patch(self, logging, 'basicConfig', my_basic_config)
3302
3303 log_method = getattr(logging, method)
3304 if level is not None:
3305 log_method(level, "test me")
3306 else:
3307 log_method("test me")
3308
3309 # basicConfig was called with no arguments
3310 self.assertEqual(called, [((), {})])
3311
3312 def test_log(self):
3313 self._test_log('log', logging.WARNING)
3314
3315 def test_debug(self):
3316 self._test_log('debug')
3317
3318 def test_info(self):
3319 self._test_log('info')
3320
3321 def test_warning(self):
3322 self._test_log('warning')
3323
3324 def test_error(self):
3325 self._test_log('error')
3326
3327 def test_critical(self):
3328 self._test_log('critical')
3329
3330
3331class LoggerAdapterTest(unittest.TestCase):
3332
3333 def setUp(self):
3334 super(LoggerAdapterTest, self).setUp()
3335 old_handler_list = logging._handlerList[:]
3336
3337 self.recording = RecordingHandler()
3338 self.logger = logging.root
3339 self.logger.addHandler(self.recording)
3340 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3341 self.addCleanup(self.recording.close)
3342
3343 def cleanup():
3344 logging._handlerList[:] = old_handler_list
3345
3346 self.addCleanup(cleanup)
3347 self.addCleanup(logging.shutdown)
3348 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3349
3350 def test_exception(self):
3351 msg = 'testing exception: %r'
3352 exc = None
3353 try:
3354 assert False
3355 except AssertionError as e:
3356 exc = e
3357 self.adapter.exception(msg, self.recording)
3358
3359 self.assertEqual(len(self.recording.records), 1)
3360 record = self.recording.records[0]
3361 self.assertEqual(record.levelno, logging.ERROR)
3362 self.assertEqual(record.msg, msg)
3363 self.assertEqual(record.args, (self.recording,))
3364 self.assertEqual(record.exc_info,
3365 (exc.__class__, exc, exc.__traceback__))
3366
3367 def test_critical(self):
3368 msg = 'critical test! %r'
3369 self.adapter.critical(msg, self.recording)
3370
3371 self.assertEqual(len(self.recording.records), 1)
3372 record = self.recording.records[0]
3373 self.assertEqual(record.levelno, logging.CRITICAL)
3374 self.assertEqual(record.msg, msg)
3375 self.assertEqual(record.args, (self.recording,))
3376
3377 def test_is_enabled_for(self):
3378 old_disable = self.adapter.logger.manager.disable
3379 self.adapter.logger.manager.disable = 33
3380 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3381 'disable', old_disable))
3382 self.assertFalse(self.adapter.isEnabledFor(32))
3383
3384 def test_has_handlers(self):
3385 self.assertTrue(self.adapter.hasHandlers())
3386
3387 for handler in self.logger.handlers:
3388 self.logger.removeHandler(handler)
3389 assert not self.logger.hasHandlers()
3390
3391 self.assertFalse(self.adapter.hasHandlers())
3392
3393
3394class LoggerTest(BaseTest):
3395
3396 def setUp(self):
3397 super(LoggerTest, self).setUp()
3398 self.recording = RecordingHandler()
3399 self.logger = logging.Logger(name='blah')
3400 self.logger.addHandler(self.recording)
3401 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3402 self.addCleanup(self.recording.close)
3403 self.addCleanup(logging.shutdown)
3404
3405 def test_set_invalid_level(self):
3406 self.assertRaises(TypeError, self.logger.setLevel, object())
3407
3408 def test_exception(self):
3409 msg = 'testing exception: %r'
3410 exc = None
3411 try:
3412 assert False
3413 except AssertionError as e:
3414 exc = e
3415 self.logger.exception(msg, self.recording)
3416
3417 self.assertEqual(len(self.recording.records), 1)
3418 record = self.recording.records[0]
3419 self.assertEqual(record.levelno, logging.ERROR)
3420 self.assertEqual(record.msg, msg)
3421 self.assertEqual(record.args, (self.recording,))
3422 self.assertEqual(record.exc_info,
3423 (exc.__class__, exc, exc.__traceback__))
3424
3425 def test_log_invalid_level_with_raise(self):
3426 old_raise = logging.raiseExceptions
3427 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3428
3429 logging.raiseExceptions = True
3430 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3431
3432 def test_log_invalid_level_no_raise(self):
3433 old_raise = logging.raiseExceptions
3434 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3435
3436 logging.raiseExceptions = False
3437 self.logger.log('10', 'test message') # no exception happens
3438
3439 def test_find_caller_with_stack_info(self):
3440 called = []
3441 patch(self, logging.traceback, 'print_stack',
3442 lambda f, file: called.append(file.getvalue()))
3443
3444 self.logger.findCaller(stack_info=True)
3445
3446 self.assertEqual(len(called), 1)
3447 self.assertEqual('Stack (most recent call last):\n', called[0])
3448
3449 def test_make_record_with_extra_overwrite(self):
3450 name = 'my record'
3451 level = 13
3452 fn = lno = msg = args = exc_info = func = sinfo = None
3453 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3454 exc_info, func, sinfo)
3455
3456 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3457 extra = {key: 'some value'}
3458 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3459 fn, lno, msg, args, exc_info,
3460 extra=extra, sinfo=sinfo)
3461
3462 def test_make_record_with_extra_no_overwrite(self):
3463 name = 'my record'
3464 level = 13
3465 fn = lno = msg = args = exc_info = func = sinfo = None
3466 extra = {'valid_key': 'some value'}
3467 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3468 exc_info, extra=extra, sinfo=sinfo)
3469 self.assertIn('valid_key', result.__dict__)
3470
3471 def test_has_handlers(self):
3472 self.assertTrue(self.logger.hasHandlers())
3473
3474 for handler in self.logger.handlers:
3475 self.logger.removeHandler(handler)
3476 assert not self.logger.hasHandlers()
3477
3478 self.assertFalse(self.logger.hasHandlers())
3479
3480 def test_has_handlers_no_propagate(self):
3481 child_logger = logging.getLogger('blah.child')
3482 child_logger.propagate = False
3483 assert child_logger.handlers == []
3484
3485 self.assertFalse(child_logger.hasHandlers())
3486
3487 def test_is_enabled_for(self):
3488 old_disable = self.logger.manager.disable
3489 self.logger.manager.disable = 23
3490 self.addCleanup(lambda: setattr(self.logger.manager,
3491 'disable', old_disable))
3492 self.assertFalse(self.logger.isEnabledFor(22))
3493
3494
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003495class BaseFileTest(BaseTest):
3496 "Base class for handler tests that write log files"
3497
3498 def setUp(self):
3499 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003500 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3501 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003502 self.rmfiles = []
3503
3504 def tearDown(self):
3505 for fn in self.rmfiles:
3506 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003507 if os.path.exists(self.fn):
3508 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003509 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003510
3511 def assertLogFile(self, filename):
3512 "Assert a log file is there and register it for deletion"
3513 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003514 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003515 self.rmfiles.append(filename)
3516
3517
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003518class FileHandlerTest(BaseFileTest):
3519 def test_delay(self):
3520 os.unlink(self.fn)
3521 fh = logging.FileHandler(self.fn, delay=True)
3522 self.assertIsNone(fh.stream)
3523 self.assertFalse(os.path.exists(self.fn))
3524 fh.handle(logging.makeLogRecord({}))
3525 self.assertIsNotNone(fh.stream)
3526 self.assertTrue(os.path.exists(self.fn))
3527 fh.close()
3528
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003529class RotatingFileHandlerTest(BaseFileTest):
3530 def next_rec(self):
3531 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3532 self.next_message(), None, None, None)
3533
3534 def test_should_not_rollover(self):
3535 # If maxbytes is zero rollover never occurs
3536 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3537 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003538 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003539
3540 def test_should_rollover(self):
3541 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3542 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003543 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003544
3545 def test_file_created(self):
3546 # checks that the file is created and assumes it was created
3547 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003548 rh = logging.handlers.RotatingFileHandler(self.fn)
3549 rh.emit(self.next_rec())
3550 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003551 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003552
3553 def test_rollover_filenames(self):
3554 rh = logging.handlers.RotatingFileHandler(
3555 self.fn, backupCount=2, maxBytes=1)
3556 rh.emit(self.next_rec())
3557 self.assertLogFile(self.fn)
3558 rh.emit(self.next_rec())
3559 self.assertLogFile(self.fn + ".1")
3560 rh.emit(self.next_rec())
3561 self.assertLogFile(self.fn + ".2")
3562 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003563 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003564
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003565class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003566 # other test methods added below
3567 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003568 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3569 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003570 r = logging.makeLogRecord({'msg': 'testing'})
3571 fh.emit(r)
3572 self.assertLogFile(self.fn)
3573 time.sleep(1.0)
3574 fh.emit(r)
3575 fh.close()
3576 # At this point, we should have a recent rotated file which we
3577 # can test for the existence of. However, in practice, on some
3578 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003579 # in time to go to look for the log file. So, we go back a fair
3580 # bit, and stop as soon as we see a rotated file. In theory this
3581 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003582 found = False
3583 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003584 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003585 for secs in range(GO_BACK):
3586 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003587 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3588 found = os.path.exists(fn)
3589 if found:
3590 self.rmfiles.append(fn)
3591 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003592 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3593 if not found:
3594 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003595 dn, fn = os.path.split(self.fn)
3596 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003597 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3598 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajipfd285022011-05-07 17:01:22 +01003599 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003600
Vinay Sajip0372e102011-05-05 12:59:14 +01003601 def test_invalid(self):
3602 assertRaises = self.assertRaises
3603 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003604 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003605 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003606 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003607 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003608 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003609
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003610def secs(**kw):
3611 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3612
3613for when, exp in (('S', 1),
3614 ('M', 60),
3615 ('H', 60 * 60),
3616 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003617 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003618 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003619 ('W0', secs(days=4, hours=24)),
3620 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003621 def test_compute_rollover(self, when=when, exp=exp):
3622 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003623 self.fn, when=when, interval=1, backupCount=0, utc=True)
3624 currentTime = 0.0
3625 actual = rh.computeRollover(currentTime)
3626 if exp != actual:
3627 # Failures occur on some systems for MIDNIGHT and W0.
3628 # Print detailed calculation for MIDNIGHT so we can try to see
3629 # what's going on
3630 import time
3631 if when == 'MIDNIGHT':
3632 try:
3633 if rh.utc:
3634 t = time.gmtime(currentTime)
3635 else:
3636 t = time.localtime(currentTime)
3637 currentHour = t[3]
3638 currentMinute = t[4]
3639 currentSecond = t[5]
3640 # r is the number of seconds left between now and midnight
3641 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3642 currentMinute) * 60 +
3643 currentSecond)
3644 result = currentTime + r
3645 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3646 print('currentHour: %s' % currentHour, file=sys.stderr)
3647 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3648 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3649 print('r: %s' % r, file=sys.stderr)
3650 print('result: %s' % result, file=sys.stderr)
3651 except Exception:
3652 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3653 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003654 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003655 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3656
Vinay Sajip60ccd822011-05-09 17:32:09 +01003657
3658@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3659class NTEventLogHandlerTest(BaseTest):
3660 def test_basic(self):
3661 logtype = 'Application'
3662 elh = win32evtlog.OpenEventLog(None, logtype)
3663 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3664 h = logging.handlers.NTEventLogHandler('test_logging')
3665 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3666 h.handle(r)
3667 h.close()
3668 # Now see if the event is recorded
3669 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3670 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3671 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3672 found = False
3673 GO_BACK = 100
3674 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3675 for e in events:
3676 if e.SourceName != 'test_logging':
3677 continue
3678 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3679 if msg != 'Test Log Message\r\n':
3680 continue
3681 found = True
3682 break
3683 msg = 'Record not found in event log, went back %d records' % GO_BACK
3684 self.assertTrue(found, msg=msg)
3685
Christian Heimes180510d2008-03-03 19:15:45 +00003686# Set the locale to the platform-dependent default. I have no idea
3687# why the test does this, but in any case we save the current locale
3688# first and restore it at the end.
3689@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003690def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003691 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003692 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003693 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3694 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3695 ManagerTest, FormatterTest, BufferingFormatterTest,
3696 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3697 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3698 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3699 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003700 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003701 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003702 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003703 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003704
Christian Heimes180510d2008-03-03 19:15:45 +00003705if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003706 test_main()