blob: 5d84114f0cdbd8e776a87a502402f2a38be7ca10 [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()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100161 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000162 for actual, expected in zip(actual_lines, expected_values):
163 match = pat.search(actual)
164 if not match:
165 self.fail("Log line does not match expected pattern:\n" +
166 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000167 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000168 s = stream.read()
169 if s:
170 self.fail("Remaining output at end of log stream:\n" + s)
171
172 def next_message(self):
173 """Generate a message consisting solely of an auto-incrementing
174 integer."""
175 self.message_num += 1
176 return "%d" % self.message_num
177
178
179class BuiltinLevelsTest(BaseTest):
180 """Test builtin levels and their inheritance."""
181
182 def test_flat(self):
183 #Logging levels in a flat logger namespace.
184 m = self.next_message
185
186 ERR = logging.getLogger("ERR")
187 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000188 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000189 INF.setLevel(logging.INFO)
190 DEB = logging.getLogger("DEB")
191 DEB.setLevel(logging.DEBUG)
192
193 # These should log.
194 ERR.log(logging.CRITICAL, m())
195 ERR.error(m())
196
197 INF.log(logging.CRITICAL, m())
198 INF.error(m())
199 INF.warn(m())
200 INF.info(m())
201
202 DEB.log(logging.CRITICAL, m())
203 DEB.error(m())
204 DEB.warn (m())
205 DEB.info (m())
206 DEB.debug(m())
207
208 # These should not log.
209 ERR.warn(m())
210 ERR.info(m())
211 ERR.debug(m())
212
213 INF.debug(m())
214
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000215 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000216 ('ERR', 'CRITICAL', '1'),
217 ('ERR', 'ERROR', '2'),
218 ('INF', 'CRITICAL', '3'),
219 ('INF', 'ERROR', '4'),
220 ('INF', 'WARNING', '5'),
221 ('INF', 'INFO', '6'),
222 ('DEB', 'CRITICAL', '7'),
223 ('DEB', 'ERROR', '8'),
224 ('DEB', 'WARNING', '9'),
225 ('DEB', 'INFO', '10'),
226 ('DEB', 'DEBUG', '11'),
227 ])
228
229 def test_nested_explicit(self):
230 # Logging levels in a nested namespace, all explicitly set.
231 m = self.next_message
232
233 INF = logging.getLogger("INF")
234 INF.setLevel(logging.INFO)
235 INF_ERR = logging.getLogger("INF.ERR")
236 INF_ERR.setLevel(logging.ERROR)
237
238 # These should log.
239 INF_ERR.log(logging.CRITICAL, m())
240 INF_ERR.error(m())
241
242 # These should not log.
243 INF_ERR.warn(m())
244 INF_ERR.info(m())
245 INF_ERR.debug(m())
246
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000247 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000248 ('INF.ERR', 'CRITICAL', '1'),
249 ('INF.ERR', 'ERROR', '2'),
250 ])
251
252 def test_nested_inherited(self):
253 #Logging levels in a nested namespace, inherited from parent loggers.
254 m = self.next_message
255
256 INF = logging.getLogger("INF")
257 INF.setLevel(logging.INFO)
258 INF_ERR = logging.getLogger("INF.ERR")
259 INF_ERR.setLevel(logging.ERROR)
260 INF_UNDEF = logging.getLogger("INF.UNDEF")
261 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
262 UNDEF = logging.getLogger("UNDEF")
263
264 # These should log.
265 INF_UNDEF.log(logging.CRITICAL, m())
266 INF_UNDEF.error(m())
267 INF_UNDEF.warn(m())
268 INF_UNDEF.info(m())
269 INF_ERR_UNDEF.log(logging.CRITICAL, m())
270 INF_ERR_UNDEF.error(m())
271
272 # These should not log.
273 INF_UNDEF.debug(m())
274 INF_ERR_UNDEF.warn(m())
275 INF_ERR_UNDEF.info(m())
276 INF_ERR_UNDEF.debug(m())
277
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000278 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000279 ('INF.UNDEF', 'CRITICAL', '1'),
280 ('INF.UNDEF', 'ERROR', '2'),
281 ('INF.UNDEF', 'WARNING', '3'),
282 ('INF.UNDEF', 'INFO', '4'),
283 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
284 ('INF.ERR.UNDEF', 'ERROR', '6'),
285 ])
286
287 def test_nested_with_virtual_parent(self):
288 # Logging levels when some parent does not exist yet.
289 m = self.next_message
290
291 INF = logging.getLogger("INF")
292 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
293 CHILD = logging.getLogger("INF.BADPARENT")
294 INF.setLevel(logging.INFO)
295
296 # These should log.
297 GRANDCHILD.log(logging.FATAL, m())
298 GRANDCHILD.info(m())
299 CHILD.log(logging.FATAL, m())
300 CHILD.info(m())
301
302 # These should not log.
303 GRANDCHILD.debug(m())
304 CHILD.debug(m())
305
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000306 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000307 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
308 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
309 ('INF.BADPARENT', 'CRITICAL', '3'),
310 ('INF.BADPARENT', 'INFO', '4'),
311 ])
312
313
314class BasicFilterTest(BaseTest):
315
316 """Test the bundled Filter class."""
317
318 def test_filter(self):
319 # Only messages satisfying the specified criteria pass through the
320 # filter.
321 filter_ = logging.Filter("spam.eggs")
322 handler = self.root_logger.handlers[0]
323 try:
324 handler.addFilter(filter_)
325 spam = logging.getLogger("spam")
326 spam_eggs = logging.getLogger("spam.eggs")
327 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
328 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
329
330 spam.info(self.next_message())
331 spam_eggs.info(self.next_message()) # Good.
332 spam_eggs_fish.info(self.next_message()) # Good.
333 spam_bakedbeans.info(self.next_message())
334
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000335 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000336 ('spam.eggs', 'INFO', '2'),
337 ('spam.eggs.fish', 'INFO', '3'),
338 ])
339 finally:
340 handler.removeFilter(filter_)
341
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000342 def test_callable_filter(self):
343 # Only messages satisfying the specified criteria pass through the
344 # filter.
345
346 def filterfunc(record):
347 parts = record.name.split('.')
348 prefix = '.'.join(parts[:2])
349 return prefix == 'spam.eggs'
350
351 handler = self.root_logger.handlers[0]
352 try:
353 handler.addFilter(filterfunc)
354 spam = logging.getLogger("spam")
355 spam_eggs = logging.getLogger("spam.eggs")
356 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
357 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
358
359 spam.info(self.next_message())
360 spam_eggs.info(self.next_message()) # Good.
361 spam_eggs_fish.info(self.next_message()) # Good.
362 spam_bakedbeans.info(self.next_message())
363
364 self.assert_log_lines([
365 ('spam.eggs', 'INFO', '2'),
366 ('spam.eggs.fish', 'INFO', '3'),
367 ])
368 finally:
369 handler.removeFilter(filterfunc)
370
Vinay Sajip985ef872011-04-26 19:34:04 +0100371 def test_empty_filter(self):
372 f = logging.Filter()
373 r = logging.makeLogRecord({'name': 'spam.eggs'})
374 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000375
376#
377# First, we define our levels. There can be as many as you want - the only
378# limitations are that they should be integers, the lowest should be > 0 and
379# larger values mean less information being logged. If you need specific
380# level values which do not fit into these limitations, you can use a
381# mapping dictionary to convert between your application levels and the
382# logging system.
383#
384SILENT = 120
385TACITURN = 119
386TERSE = 118
387EFFUSIVE = 117
388SOCIABLE = 116
389VERBOSE = 115
390TALKATIVE = 114
391GARRULOUS = 113
392CHATTERBOX = 112
393BORING = 111
394
395LEVEL_RANGE = range(BORING, SILENT + 1)
396
397#
398# Next, we define names for our levels. You don't need to do this - in which
399# case the system will use "Level n" to denote the text for the level.
400#
401my_logging_levels = {
402 SILENT : 'Silent',
403 TACITURN : 'Taciturn',
404 TERSE : 'Terse',
405 EFFUSIVE : 'Effusive',
406 SOCIABLE : 'Sociable',
407 VERBOSE : 'Verbose',
408 TALKATIVE : 'Talkative',
409 GARRULOUS : 'Garrulous',
410 CHATTERBOX : 'Chatterbox',
411 BORING : 'Boring',
412}
413
414class GarrulousFilter(logging.Filter):
415
416 """A filter which blocks garrulous messages."""
417
418 def filter(self, record):
419 return record.levelno != GARRULOUS
420
421class VerySpecificFilter(logging.Filter):
422
423 """A filter which blocks sociable and taciturn messages."""
424
425 def filter(self, record):
426 return record.levelno not in [SOCIABLE, TACITURN]
427
428
429class CustomLevelsAndFiltersTest(BaseTest):
430
431 """Test various filtering possibilities with custom logging levels."""
432
433 # Skip the logger name group.
434 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
435
436 def setUp(self):
437 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000438 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000439 logging.addLevelName(k, v)
440
441 def log_at_all_levels(self, logger):
442 for lvl in LEVEL_RANGE:
443 logger.log(lvl, self.next_message())
444
445 def test_logger_filter(self):
446 # Filter at logger level.
447 self.root_logger.setLevel(VERBOSE)
448 # Levels >= 'Verbose' are good.
449 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000450 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000451 ('Verbose', '5'),
452 ('Sociable', '6'),
453 ('Effusive', '7'),
454 ('Terse', '8'),
455 ('Taciturn', '9'),
456 ('Silent', '10'),
457 ])
458
459 def test_handler_filter(self):
460 # Filter at handler level.
461 self.root_logger.handlers[0].setLevel(SOCIABLE)
462 try:
463 # Levels >= 'Sociable' are good.
464 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000465 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000466 ('Sociable', '6'),
467 ('Effusive', '7'),
468 ('Terse', '8'),
469 ('Taciturn', '9'),
470 ('Silent', '10'),
471 ])
472 finally:
473 self.root_logger.handlers[0].setLevel(logging.NOTSET)
474
475 def test_specific_filters(self):
476 # Set a specific filter object on the handler, and then add another
477 # filter object on the logger itself.
478 handler = self.root_logger.handlers[0]
479 specific_filter = None
480 garr = GarrulousFilter()
481 handler.addFilter(garr)
482 try:
483 self.log_at_all_levels(self.root_logger)
484 first_lines = [
485 # Notice how 'Garrulous' is missing
486 ('Boring', '1'),
487 ('Chatterbox', '2'),
488 ('Talkative', '4'),
489 ('Verbose', '5'),
490 ('Sociable', '6'),
491 ('Effusive', '7'),
492 ('Terse', '8'),
493 ('Taciturn', '9'),
494 ('Silent', '10'),
495 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000496 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000497
498 specific_filter = VerySpecificFilter()
499 self.root_logger.addFilter(specific_filter)
500 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000501 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000502 # Not only 'Garrulous' is still missing, but also 'Sociable'
503 # and 'Taciturn'
504 ('Boring', '11'),
505 ('Chatterbox', '12'),
506 ('Talkative', '14'),
507 ('Verbose', '15'),
508 ('Effusive', '17'),
509 ('Terse', '18'),
510 ('Silent', '20'),
511 ])
512 finally:
513 if specific_filter:
514 self.root_logger.removeFilter(specific_filter)
515 handler.removeFilter(garr)
516
517
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100518class HandlerTest(BaseTest):
519 def test_name(self):
520 h = logging.Handler()
521 h.name = 'generic'
522 self.assertEqual(h.name, 'generic')
523 h.name = 'anothergeneric'
524 self.assertEqual(h.name, 'anothergeneric')
525 self.assertRaises(NotImplementedError, h.emit, None)
526
Vinay Sajip5a35b062011-04-27 11:31:14 +0100527 def test_builtin_handlers(self):
528 # We can't actually *use* too many handlers in the tests,
529 # but we can try instantiating them with various options
530 if sys.platform in ('linux2', 'darwin'):
531 for existing in (True, False):
532 fd, fn = tempfile.mkstemp()
533 os.close(fd)
534 if not existing:
535 os.unlink(fn)
536 h = logging.handlers.WatchedFileHandler(fn, delay=True)
537 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100538 dev, ino = h.dev, h.ino
539 self.assertNotEqual(dev, -1)
540 self.assertNotEqual(ino, -1)
541 r = logging.makeLogRecord({'msg': 'Test'})
542 h.handle(r)
543 # Now remove the file.
544 os.unlink(fn)
545 self.assertFalse(os.path.exists(fn))
546 # The next call should recreate the file.
547 h.handle(r)
548 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100549 else:
550 self.assertEqual(h.dev, -1)
551 self.assertEqual(h.ino, -1)
552 h.close()
553 if existing:
554 os.unlink(fn)
555 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100556 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100557 else:
558 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100559 try:
560 h = logging.handlers.SysLogHandler(sockname)
561 self.assertEqual(h.facility, h.LOG_USER)
562 self.assertTrue(h.unixsocket)
563 h.close()
564 except socket.error: # syslogd might not be available
565 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100566 for method in ('GET', 'POST', 'PUT'):
567 if method == 'PUT':
568 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
569 'localhost', '/log', method)
570 else:
571 h = logging.handlers.HTTPHandler('localhost', '/log', method)
572 h.close()
573 h = logging.handlers.BufferingHandler(0)
574 r = logging.makeLogRecord({})
575 self.assertTrue(h.shouldFlush(r))
576 h.close()
577 h = logging.handlers.BufferingHandler(1)
578 self.assertFalse(h.shouldFlush(r))
579 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100580
581class BadStream(object):
582 def write(self, data):
583 raise RuntimeError('deliberate mistake')
584
585class TestStreamHandler(logging.StreamHandler):
586 def handleError(self, record):
587 self.error_record = record
588
589class StreamHandlerTest(BaseTest):
590 def test_error_handling(self):
591 h = TestStreamHandler(BadStream())
592 r = logging.makeLogRecord({})
593 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100594 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100595 try:
596 h.handle(r)
597 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100598 h = logging.StreamHandler(BadStream())
599 sys.stderr = sio = io.StringIO()
600 h.handle(r)
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100601 self.assertIn('\nRuntimeError: deliberate mistake\n',
602 sio.getvalue())
Vinay Sajip985ef872011-04-26 19:34:04 +0100603 logging.raiseExceptions = False
604 sys.stderr = sio = io.StringIO()
605 h.handle(r)
606 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100607 finally:
608 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100609 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100610
Vinay Sajip7367d082011-05-02 13:17:27 +0100611# -- The following section could be moved into a server_helper.py module
612# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100613
Vinay Sajipce7c9782011-05-17 07:15:53 +0100614if threading:
615 class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100616 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100617 This derived class has had to be created because smtpd does not
618 support use of custom channel maps, although they are allowed by
619 asyncore's design. Issue #11959 has been raised to address this,
620 and if resolved satisfactorily, some of this code can be removed.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100621 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100622 def __init__(self, server, conn, addr, sockmap):
623 asynchat.async_chat.__init__(self, conn, sockmap)
624 self.smtp_server = server
625 self.conn = conn
626 self.addr = addr
627 self.received_lines = []
628 self.smtp_state = self.COMMAND
629 self.seen_greeting = ''
630 self.mailfrom = None
631 self.rcpttos = []
632 self.received_data = ''
633 self.fqdn = socket.getfqdn()
634 self.num_bytes = 0
635 try:
636 self.peer = conn.getpeername()
637 except socket.error as err:
638 # a race condition may occur if the other end is closing
639 # before we can get the peername
640 self.close()
641 if err.args[0] != errno.ENOTCONN:
642 raise
643 return
644 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
645 self.set_terminator(b'\r\n')
Vinay Sajipa463d252011-04-30 21:52:48 +0100646
Vinay Sajip314b92b2011-05-02 14:31:16 +0100647
Vinay Sajipce7c9782011-05-17 07:15:53 +0100648 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100649 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100650 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100651
Vinay Sajipce7c9782011-05-17 07:15:53 +0100652 :param addr: A (host, port) tuple which the server listens on.
653 You can specify a port value of zero: the server's
654 *port* attribute will hold the actual port number
655 used, which can be used in client connections.
656 :param handler: A callable which will be called to process
657 incoming messages. The handler will be passed
658 the client address tuple, who the message is from,
659 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100660 :param poll_interval: The interval, in seconds, used in the underlying
661 :func:`select` or :func:`poll` call by
662 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100663 :param sockmap: A dictionary which will be used to hold
664 :class:`asyncore.dispatcher` instances used by
665 :func:`asyncore.loop`. This avoids changing the
666 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100667 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100668 channel_class = TestSMTPChannel
669
670 def __init__(self, addr, handler, poll_interval, sockmap):
671 self._localaddr = addr
672 self._remoteaddr = None
673 self.sockmap = sockmap
674 asyncore.dispatcher.__init__(self, map=sockmap)
675 try:
676 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
677 sock.setblocking(0)
678 self.set_socket(sock, map=sockmap)
679 # try to re-use a server port if possible
680 self.set_reuse_addr()
681 self.bind(addr)
682 self.port = sock.getsockname()[1]
683 self.listen(5)
684 except:
685 self.close()
Vinay Sajipe73afad2011-05-10 07:48:28 +0100686 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +0100687 self._handler = handler
688 self._thread = None
689 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100690
Vinay Sajipce7c9782011-05-17 07:15:53 +0100691 def handle_accepted(self, conn, addr):
692 """
693 Redefined only because the base class does not pass in a
694 map, forcing use of a global in :mod:`asyncore`.
695 """
696 channel = self.channel_class(self, conn, addr, self.sockmap)
Vinay Sajip314b92b2011-05-02 14:31:16 +0100697
Vinay Sajipce7c9782011-05-17 07:15:53 +0100698 def process_message(self, peer, mailfrom, rcpttos, data):
699 """
700 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100701
Vinay Sajipce7c9782011-05-17 07:15:53 +0100702 Typically, this will be a test case method.
703 :param peer: The client (host, port) tuple.
704 :param mailfrom: The address of the sender.
705 :param rcpttos: The addresses of the recipients.
706 :param data: The message.
707 """
708 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100709
Vinay Sajipce7c9782011-05-17 07:15:53 +0100710 def start(self):
711 """
712 Start the server running on a separate daemon thread.
713 """
714 self._thread = t = threading.Thread(target=self.serve_forever,
715 args=(self.poll_interval,))
716 t.setDaemon(True)
717 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100718
Vinay Sajipce7c9782011-05-17 07:15:53 +0100719 def serve_forever(self, poll_interval):
720 """
721 Run the :mod:`asyncore` loop until normal termination
722 conditions arise.
723 :param poll_interval: The interval, in seconds, used in the underlying
724 :func:`select` or :func:`poll` call by
725 :func:`asyncore.loop`.
726 """
727 try:
728 asyncore.loop(poll_interval, map=self.sockmap)
729 except select.error:
730 # On FreeBSD 8, closing the server repeatably
731 # raises this error. We swallow it if the
732 # server has been closed.
733 if self.connected or self.accepting:
734 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100735
Vinay Sajipce7c9782011-05-17 07:15:53 +0100736 def stop(self, timeout=None):
737 """
738 Stop the thread by closing the server instance.
739 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100740
Vinay Sajipce7c9782011-05-17 07:15:53 +0100741 :param timeout: How long to wait for the server thread
742 to terminate.
743 """
744 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100745 self._thread.join(timeout)
746 self._thread = None
Vinay Sajip7367d082011-05-02 13:17:27 +0100747
Vinay Sajipce7c9782011-05-17 07:15:53 +0100748 class ControlMixin(object):
749 """
750 This mixin is used to start a server on a separate thread, and
751 shut it down programmatically. Request handling is simplified - instead
752 of needing to derive a suitable RequestHandler subclass, you just
753 provide a callable which will be passed each received request to be
754 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100755
Vinay Sajipce7c9782011-05-17 07:15:53 +0100756 :param handler: A handler callable which will be called with a
757 single parameter - the request - in order to
758 process the request. This handler is called on the
759 server thread, effectively meaning that requests are
760 processed serially. While not quite Web scale ;-),
761 this should be fine for testing applications.
762 :param poll_interval: The polling interval in seconds.
763 """
764 def __init__(self, handler, poll_interval):
765 self._thread = None
766 self.poll_interval = poll_interval
767 self._handler = handler
768 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100769
Vinay Sajipce7c9782011-05-17 07:15:53 +0100770 def start(self):
771 """
772 Create a daemon thread to run the server, and start it.
773 """
774 self._thread = t = threading.Thread(target=self.serve_forever,
775 args=(self.poll_interval,))
776 t.setDaemon(True)
777 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100778
Vinay Sajipce7c9782011-05-17 07:15:53 +0100779 def serve_forever(self, poll_interval):
780 """
781 Run the server. Set the ready flag before entering the
782 service loop.
783 """
784 self.ready.set()
785 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100786
Vinay Sajipce7c9782011-05-17 07:15:53 +0100787 def stop(self, timeout=None):
788 """
789 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100790
Vinay Sajipce7c9782011-05-17 07:15:53 +0100791 :param timeout: How long to wait for the server thread
792 to terminate.
793 """
794 self.shutdown()
795 if self._thread is not None:
796 self._thread.join(timeout)
797 self._thread = None
798 self.server_close()
799 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100800
Vinay Sajipce7c9782011-05-17 07:15:53 +0100801 class TestHTTPServer(ControlMixin, HTTPServer):
802 """
803 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100804
Vinay Sajipce7c9782011-05-17 07:15:53 +0100805 :param addr: A tuple with the IP address and port to listen on.
806 :param handler: A handler callable which will be called with a
807 single parameter - the request - in order to
808 process the request.
809 :param poll_interval: The polling interval in seconds.
810 :param log: Pass ``True`` to enable log messages.
811 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100812 def __init__(self, addr, handler, poll_interval=0.5,
813 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100814 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
815 def __getattr__(self, name, default=None):
816 if name.startswith('do_'):
817 return self.process_request
818 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100819
Vinay Sajipce7c9782011-05-17 07:15:53 +0100820 def process_request(self):
821 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100822
Vinay Sajipce7c9782011-05-17 07:15:53 +0100823 def log_message(self, format, *args):
824 if log:
825 super(DelegatingHTTPRequestHandler,
826 self).log_message(format, *args)
827 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
828 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100829 self.sslctx = sslctx
830
831 def get_request(self):
832 try:
833 sock, addr = self.socket.accept()
834 if self.sslctx:
835 sock = self.sslctx.wrap_socket(sock, server_side=True)
836 except socket.error as e:
837 # socket errors are silenced by the caller, print them here
838 sys.stderr.write("Got an error:\n%s\n" % e)
839 raise
840 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100841
Vinay Sajipce7c9782011-05-17 07:15:53 +0100842 class TestTCPServer(ControlMixin, ThreadingTCPServer):
843 """
844 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100845
Vinay Sajipce7c9782011-05-17 07:15:53 +0100846 :param addr: A tuple with the IP address and port to listen on.
847 :param handler: A handler callable which will be called with a single
848 parameter - the request - in order to process the request.
849 :param poll_interval: The polling interval in seconds.
850 :bind_and_activate: If True (the default), binds the server and starts it
851 listening. If False, you need to call
852 :meth:`server_bind` and :meth:`server_activate` at
853 some later time before calling :meth:`start`, so that
854 the server will set up the socket and listen on it.
855 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100856
Vinay Sajipce7c9782011-05-17 07:15:53 +0100857 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100858
Vinay Sajipce7c9782011-05-17 07:15:53 +0100859 def __init__(self, addr, handler, poll_interval=0.5,
860 bind_and_activate=True):
861 class DelegatingTCPRequestHandler(StreamRequestHandler):
862
863 def handle(self):
864 self.server._handler(self)
865 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
866 bind_and_activate)
867 ControlMixin.__init__(self, handler, poll_interval)
868
869 def server_bind(self):
870 super(TestTCPServer, self).server_bind()
871 self.port = self.socket.getsockname()[1]
872
873 class TestUDPServer(ControlMixin, ThreadingUDPServer):
874 """
875 A UDP server which is controllable using :class:`ControlMixin`.
876
877 :param addr: A tuple with the IP address and port to listen on.
878 :param handler: A handler callable which will be called with a
879 single parameter - the request - in order to
880 process the request.
881 :param poll_interval: The polling interval for shutdown requests,
882 in seconds.
883 :bind_and_activate: If True (the default), binds the server and
884 starts it listening. If False, you need to
885 call :meth:`server_bind` and
886 :meth:`server_activate` at some later time
887 before calling :meth:`start`, so that the server will
888 set up the socket and listen on it.
889 """
Vinay Sajip3ef12292011-05-23 23:00:42 +0100890 def __init__(self, addr, handler, poll_interval=0.5,
891 bind_and_activate=True):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100892 class DelegatingUDPRequestHandler(DatagramRequestHandler):
893
894 def handle(self):
895 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100896
897 def finish(self):
898 data = self.wfile.getvalue()
Vinay Sajip3ef12292011-05-23 23:00:42 +0100899 if data:
900 try:
901 super(DelegatingUDPRequestHandler, self).finish()
902 except socket.error:
903 if not self.server._closed:
904 raise
Vinay Sajipba980db2011-05-23 21:37:54 +0100905
Vinay Sajip3ef12292011-05-23 23:00:42 +0100906 ThreadingUDPServer.__init__(self, addr,
907 DelegatingUDPRequestHandler,
Vinay Sajipce7c9782011-05-17 07:15:53 +0100908 bind_and_activate)
909 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100910 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100911
912 def server_bind(self):
913 super(TestUDPServer, self).server_bind()
914 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100915
Vinay Sajipba980db2011-05-23 21:37:54 +0100916 def server_close(self):
917 super(TestUDPServer, self).server_close()
918 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100919
920# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100921
Vinay Sajipce7c9782011-05-17 07:15:53 +0100922@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100923class SMTPHandlerTest(BaseTest):
924 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100925 sockmap = {}
926 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
927 sockmap)
928 server.start()
929 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100930 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
931 self.assertEqual(h.toaddrs, ['you'])
932 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100933 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100934 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100935 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100936 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100937 server.stop()
938 self.assertEqual(len(self.messages), 1)
939 peer, mailfrom, rcpttos, data = self.messages[0]
940 self.assertEqual(mailfrom, 'me')
941 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100942 self.assertIn('\nSubject: Log\n', data)
Vinay Sajipa463d252011-04-30 21:52:48 +0100943 self.assertTrue(data.endswith('\n\nHello'))
944 h.close()
945
946 def process_message(self, *args):
947 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100948 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100949
Christian Heimes180510d2008-03-03 19:15:45 +0000950class MemoryHandlerTest(BaseTest):
951
952 """Tests for the MemoryHandler."""
953
954 # Do not bother with a logger name group.
955 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
956
957 def setUp(self):
958 BaseTest.setUp(self)
959 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
960 self.root_hdlr)
961 self.mem_logger = logging.getLogger('mem')
962 self.mem_logger.propagate = 0
963 self.mem_logger.addHandler(self.mem_hdlr)
964
965 def tearDown(self):
966 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000967 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000968
969 def test_flush(self):
970 # The memory handler flushes to its target handler based on specific
971 # criteria (message count and message level).
972 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000973 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000974 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000975 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000976 # This will flush because the level is >= logging.WARNING
977 self.mem_logger.warn(self.next_message())
978 lines = [
979 ('DEBUG', '1'),
980 ('INFO', '2'),
981 ('WARNING', '3'),
982 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000983 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000984 for n in (4, 14):
985 for i in range(9):
986 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000987 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000988 # This will flush because it's the 10th message since the last
989 # flush.
990 self.mem_logger.debug(self.next_message())
991 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000992 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000993
994 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000995 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000996
997
998class ExceptionFormatter(logging.Formatter):
999 """A special exception formatter."""
1000 def formatException(self, ei):
1001 return "Got a [%s]" % ei[0].__name__
1002
1003
1004class ConfigFileTest(BaseTest):
1005
1006 """Reading logging config from a .ini-style config file."""
1007
1008 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1009
1010 # config0 is a standard configuration.
1011 config0 = """
1012 [loggers]
1013 keys=root
1014
1015 [handlers]
1016 keys=hand1
1017
1018 [formatters]
1019 keys=form1
1020
1021 [logger_root]
1022 level=WARNING
1023 handlers=hand1
1024
1025 [handler_hand1]
1026 class=StreamHandler
1027 level=NOTSET
1028 formatter=form1
1029 args=(sys.stdout,)
1030
1031 [formatter_form1]
1032 format=%(levelname)s ++ %(message)s
1033 datefmt=
1034 """
1035
1036 # config1 adds a little to the standard configuration.
1037 config1 = """
1038 [loggers]
1039 keys=root,parser
1040
1041 [handlers]
1042 keys=hand1
1043
1044 [formatters]
1045 keys=form1
1046
1047 [logger_root]
1048 level=WARNING
1049 handlers=
1050
1051 [logger_parser]
1052 level=DEBUG
1053 handlers=hand1
1054 propagate=1
1055 qualname=compiler.parser
1056
1057 [handler_hand1]
1058 class=StreamHandler
1059 level=NOTSET
1060 formatter=form1
1061 args=(sys.stdout,)
1062
1063 [formatter_form1]
1064 format=%(levelname)s ++ %(message)s
1065 datefmt=
1066 """
1067
Vinay Sajip3f84b072011-03-07 17:49:33 +00001068 # config1a moves the handler to the root.
1069 config1a = """
1070 [loggers]
1071 keys=root,parser
1072
1073 [handlers]
1074 keys=hand1
1075
1076 [formatters]
1077 keys=form1
1078
1079 [logger_root]
1080 level=WARNING
1081 handlers=hand1
1082
1083 [logger_parser]
1084 level=DEBUG
1085 handlers=
1086 propagate=1
1087 qualname=compiler.parser
1088
1089 [handler_hand1]
1090 class=StreamHandler
1091 level=NOTSET
1092 formatter=form1
1093 args=(sys.stdout,)
1094
1095 [formatter_form1]
1096 format=%(levelname)s ++ %(message)s
1097 datefmt=
1098 """
1099
Christian Heimes180510d2008-03-03 19:15:45 +00001100 # config2 has a subtle configuration error that should be reported
1101 config2 = config1.replace("sys.stdout", "sys.stbout")
1102
1103 # config3 has a less subtle configuration error
1104 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1105
1106 # config4 specifies a custom formatter class to be loaded
1107 config4 = """
1108 [loggers]
1109 keys=root
1110
1111 [handlers]
1112 keys=hand1
1113
1114 [formatters]
1115 keys=form1
1116
1117 [logger_root]
1118 level=NOTSET
1119 handlers=hand1
1120
1121 [handler_hand1]
1122 class=StreamHandler
1123 level=NOTSET
1124 formatter=form1
1125 args=(sys.stdout,)
1126
1127 [formatter_form1]
1128 class=""" + __name__ + """.ExceptionFormatter
1129 format=%(levelname)s:%(name)s:%(message)s
1130 datefmt=
1131 """
1132
Georg Brandl3dbca812008-07-23 16:10:53 +00001133 # config5 specifies a custom handler class to be loaded
1134 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1135
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001136 # config6 uses ', ' delimiters in the handlers and formatters sections
1137 config6 = """
1138 [loggers]
1139 keys=root,parser
1140
1141 [handlers]
1142 keys=hand1, hand2
1143
1144 [formatters]
1145 keys=form1, form2
1146
1147 [logger_root]
1148 level=WARNING
1149 handlers=
1150
1151 [logger_parser]
1152 level=DEBUG
1153 handlers=hand1
1154 propagate=1
1155 qualname=compiler.parser
1156
1157 [handler_hand1]
1158 class=StreamHandler
1159 level=NOTSET
1160 formatter=form1
1161 args=(sys.stdout,)
1162
1163 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001164 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001165 level=NOTSET
1166 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001167 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001168
1169 [formatter_form1]
1170 format=%(levelname)s ++ %(message)s
1171 datefmt=
1172
1173 [formatter_form2]
1174 format=%(message)s
1175 datefmt=
1176 """
1177
Vinay Sajip3f84b072011-03-07 17:49:33 +00001178 # config7 adds a compiler logger.
1179 config7 = """
1180 [loggers]
1181 keys=root,parser,compiler
1182
1183 [handlers]
1184 keys=hand1
1185
1186 [formatters]
1187 keys=form1
1188
1189 [logger_root]
1190 level=WARNING
1191 handlers=hand1
1192
1193 [logger_compiler]
1194 level=DEBUG
1195 handlers=
1196 propagate=1
1197 qualname=compiler
1198
1199 [logger_parser]
1200 level=DEBUG
1201 handlers=
1202 propagate=1
1203 qualname=compiler.parser
1204
1205 [handler_hand1]
1206 class=StreamHandler
1207 level=NOTSET
1208 formatter=form1
1209 args=(sys.stdout,)
1210
1211 [formatter_form1]
1212 format=%(levelname)s ++ %(message)s
1213 datefmt=
1214 """
1215
Christian Heimes180510d2008-03-03 19:15:45 +00001216 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001217 file = io.StringIO(textwrap.dedent(conf))
1218 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001219
1220 def test_config0_ok(self):
1221 # A simple config file which overrides the default settings.
1222 with captured_stdout() as output:
1223 self.apply_config(self.config0)
1224 logger = logging.getLogger()
1225 # Won't output anything
1226 logger.info(self.next_message())
1227 # Outputs a message
1228 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001229 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001230 ('ERROR', '2'),
1231 ], stream=output)
1232 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001233 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001234
Georg Brandl3dbca812008-07-23 16:10:53 +00001235 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001236 # A config file defining a sub-parser as well.
1237 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001238 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001239 logger = logging.getLogger("compiler.parser")
1240 # Both will output a message
1241 logger.info(self.next_message())
1242 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001243 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001244 ('INFO', '1'),
1245 ('ERROR', '2'),
1246 ], stream=output)
1247 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001248 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001249
1250 def test_config2_failure(self):
1251 # A simple config file which overrides the default settings.
1252 self.assertRaises(Exception, self.apply_config, self.config2)
1253
1254 def test_config3_failure(self):
1255 # A simple config file which overrides the default settings.
1256 self.assertRaises(Exception, self.apply_config, self.config3)
1257
1258 def test_config4_ok(self):
1259 # A config file specifying a custom formatter class.
1260 with captured_stdout() as output:
1261 self.apply_config(self.config4)
1262 logger = logging.getLogger()
1263 try:
1264 raise RuntimeError()
1265 except RuntimeError:
1266 logging.exception("just testing")
1267 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001268 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001269 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1270 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001271 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001272
Georg Brandl3dbca812008-07-23 16:10:53 +00001273 def test_config5_ok(self):
1274 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001275
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001276 def test_config6_ok(self):
1277 self.test_config1_ok(config=self.config6)
1278
Vinay Sajip3f84b072011-03-07 17:49:33 +00001279 def test_config7_ok(self):
1280 with captured_stdout() as output:
1281 self.apply_config(self.config1a)
1282 logger = logging.getLogger("compiler.parser")
1283 # See issue #11424. compiler-hyphenated sorts
1284 # between compiler and compiler.xyz and this
1285 # was preventing compiler.xyz from being included
1286 # in the child loggers of compiler because of an
1287 # overzealous loop termination condition.
1288 hyphenated = logging.getLogger('compiler-hyphenated')
1289 # All will output a message
1290 logger.info(self.next_message())
1291 logger.error(self.next_message())
1292 hyphenated.critical(self.next_message())
1293 self.assert_log_lines([
1294 ('INFO', '1'),
1295 ('ERROR', '2'),
1296 ('CRITICAL', '3'),
1297 ], stream=output)
1298 # Original logger output is empty.
1299 self.assert_log_lines([])
1300 with captured_stdout() as output:
1301 self.apply_config(self.config7)
1302 logger = logging.getLogger("compiler.parser")
1303 self.assertFalse(logger.disabled)
1304 # Both will output a message
1305 logger.info(self.next_message())
1306 logger.error(self.next_message())
1307 logger = logging.getLogger("compiler.lexer")
1308 # Both will output a message
1309 logger.info(self.next_message())
1310 logger.error(self.next_message())
1311 # Will not appear
1312 hyphenated.critical(self.next_message())
1313 self.assert_log_lines([
1314 ('INFO', '4'),
1315 ('ERROR', '5'),
1316 ('INFO', '6'),
1317 ('ERROR', '7'),
1318 ], stream=output)
1319 # Original logger output is empty.
1320 self.assert_log_lines([])
1321
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001322
Victor Stinner45df8202010-04-28 22:31:17 +00001323@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001324class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001325
Christian Heimes180510d2008-03-03 19:15:45 +00001326 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001327
Christian Heimes180510d2008-03-03 19:15:45 +00001328 def setUp(self):
1329 """Set up a TCP server to receive log messages, and a SocketHandler
1330 pointing to that server's address and port."""
1331 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001332 addr = ('localhost', 0)
1333 self.server = server = TestTCPServer(addr, self.handle_socket,
1334 0.01)
1335 server.start()
1336 server.ready.wait()
1337 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1338 server.port)
1339 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001340 self.root_logger.removeHandler(self.root_logger.handlers[0])
1341 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001342 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001343
Christian Heimes180510d2008-03-03 19:15:45 +00001344 def tearDown(self):
1345 """Shutdown the TCP server."""
1346 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001347 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001348 self.root_logger.removeHandler(self.sock_hdlr)
1349 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001350 finally:
1351 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001352
Vinay Sajip7367d082011-05-02 13:17:27 +01001353 def handle_socket(self, request):
1354 conn = request.connection
1355 while True:
1356 chunk = conn.recv(4)
1357 if len(chunk) < 4:
1358 break
1359 slen = struct.unpack(">L", chunk)[0]
1360 chunk = conn.recv(slen)
1361 while len(chunk) < slen:
1362 chunk = chunk + conn.recv(slen - len(chunk))
1363 obj = pickle.loads(chunk)
1364 record = logging.makeLogRecord(obj)
1365 self.log_output += record.msg + '\n'
1366 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001367
Christian Heimes180510d2008-03-03 19:15:45 +00001368 def test_output(self):
1369 # The log message sent to the SocketHandler is properly received.
1370 logger = logging.getLogger("tcp")
1371 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001372 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001373 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001374 self.handled.acquire()
1375 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001376
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001377 def test_noserver(self):
1378 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001379 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001380 #The logging call should try to connect, which should fail
1381 try:
1382 raise RuntimeError('Deliberate mistake')
1383 except RuntimeError:
1384 self.root_logger.exception('Never sent')
1385 self.root_logger.error('Never sent, either')
1386 now = time.time()
1387 self.assertTrue(self.sock_hdlr.retryTime > now)
1388 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1389 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001390
Vinay Sajip7367d082011-05-02 13:17:27 +01001391
1392@unittest.skipUnless(threading, 'Threading required for this test.')
1393class DatagramHandlerTest(BaseTest):
1394
1395 """Test for DatagramHandler."""
1396
1397 def setUp(self):
1398 """Set up a UDP server to receive log messages, and a DatagramHandler
1399 pointing to that server's address and port."""
1400 BaseTest.setUp(self)
1401 addr = ('localhost', 0)
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001402 self.server = server = TestUDPServer(addr, self.handle_datagram, 0.01)
Vinay Sajip7367d082011-05-02 13:17:27 +01001403 server.start()
1404 server.ready.wait()
1405 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1406 server.port)
1407 self.log_output = ''
1408 self.root_logger.removeHandler(self.root_logger.handlers[0])
1409 self.root_logger.addHandler(self.sock_hdlr)
1410 self.handled = threading.Event()
1411
1412 def tearDown(self):
1413 """Shutdown the UDP server."""
1414 try:
1415 self.server.stop(2.0)
1416 self.root_logger.removeHandler(self.sock_hdlr)
1417 self.sock_hdlr.close()
1418 finally:
1419 BaseTest.tearDown(self)
1420
1421 def handle_datagram(self, request):
1422 slen = struct.pack('>L', 0) # length of prefix
1423 packet = request.packet[len(slen):]
1424 obj = pickle.loads(packet)
1425 record = logging.makeLogRecord(obj)
1426 self.log_output += record.msg + '\n'
1427 self.handled.set()
1428
1429 def test_output(self):
1430 # The log message sent to the DatagramHandler is properly received.
1431 logger = logging.getLogger("udp")
1432 logger.error("spam")
1433 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001434 self.handled.clear()
1435 logger.error("eggs")
1436 self.handled.wait()
1437 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001438
1439
1440@unittest.skipUnless(threading, 'Threading required for this test.')
1441class SysLogHandlerTest(BaseTest):
1442
1443 """Test for SysLogHandler using UDP."""
1444
1445 def setUp(self):
1446 """Set up a UDP server to receive log messages, and a SysLogHandler
1447 pointing to that server's address and port."""
1448 BaseTest.setUp(self)
1449 addr = ('localhost', 0)
1450 self.server = server = TestUDPServer(addr, self.handle_datagram,
1451 0.01)
1452 server.start()
1453 server.ready.wait()
1454 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1455 server.port))
1456 self.log_output = ''
1457 self.root_logger.removeHandler(self.root_logger.handlers[0])
1458 self.root_logger.addHandler(self.sl_hdlr)
1459 self.handled = threading.Event()
1460
1461 def tearDown(self):
1462 """Shutdown the UDP server."""
1463 try:
1464 self.server.stop(2.0)
1465 self.root_logger.removeHandler(self.sl_hdlr)
1466 self.sl_hdlr.close()
1467 finally:
1468 BaseTest.tearDown(self)
1469
1470 def handle_datagram(self, request):
1471 self.log_output = request.packet
1472 self.handled.set()
1473
1474 def test_output(self):
1475 # The log message sent to the SysLogHandler is properly received.
1476 logger = logging.getLogger("slh")
1477 logger.error("sp\xe4m")
1478 self.handled.wait()
1479 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001480 self.handled.clear()
1481 self.sl_hdlr.append_nul = False
1482 logger.error("sp\xe4m")
1483 self.handled.wait()
1484 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001485 self.handled.clear()
1486 self.sl_hdlr.ident = "h\xe4m-"
1487 logger.error("sp\xe4m")
1488 self.handled.wait()
1489 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfh\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001490
1491
1492@unittest.skipUnless(threading, 'Threading required for this test.')
1493class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001494 """Test for HTTPHandler."""
1495
Vinay Sajip9ba87612011-05-21 11:32:15 +01001496 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1497MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1498zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1499aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1500AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
15010tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
15025LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1503piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
15042z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1505i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1506PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1507E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1508rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1509/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1510-----END RSA PRIVATE KEY-----
1511-----BEGIN CERTIFICATE-----
1512MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1513BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1514EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1515xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1516l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1517vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1518BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1519S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1520BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15214f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15221c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1523IRbTpvlHWPjsSvHz0ZOH
1524-----END CERTIFICATE-----"""
1525
Vinay Sajip7367d082011-05-02 13:17:27 +01001526 def setUp(self):
1527 """Set up an HTTP server to receive log messages, and a HTTPHandler
1528 pointing to that server's address and port."""
1529 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001530 self.handled = threading.Event()
1531
Vinay Sajip7367d082011-05-02 13:17:27 +01001532 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001533 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001534 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001535 if self.command == 'POST':
1536 try:
1537 rlen = int(request.headers['Content-Length'])
1538 self.post_data = request.rfile.read(rlen)
1539 except:
1540 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001541 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001542 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001543 self.handled.set()
1544
1545 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001546 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001547 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001548 root_logger = self.root_logger
1549 root_logger.removeHandler(self.root_logger.handlers[0])
1550 for secure in (False, True):
1551 addr = ('localhost', 0)
1552 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001553 try:
1554 import ssl
1555 fd, fn = tempfile.mkstemp()
1556 os.close(fd)
1557 with open(fn, 'w') as f:
1558 f.write(self.PEMFILE)
1559 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1560 sslctx.load_cert_chain(fn)
1561 os.unlink(fn)
1562 except ImportError:
1563 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001564 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001565 sslctx = None
1566 self.server = server = TestHTTPServer(addr, self.handle_request,
1567 0.01, sslctx=sslctx)
1568 server.start()
1569 server.ready.wait()
1570 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001571 secure_client = secure and sslctx
1572 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1573 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001574 self.log_data = None
1575 root_logger.addHandler(self.h_hdlr)
1576
1577 for method in ('GET', 'POST'):
1578 self.h_hdlr.method = method
1579 self.handled.clear()
1580 msg = "sp\xe4m"
1581 logger.error(msg)
1582 self.handled.wait()
1583 self.assertEqual(self.log_data.path, '/frob')
1584 self.assertEqual(self.command, method)
1585 if method == 'GET':
1586 d = parse_qs(self.log_data.query)
1587 else:
1588 d = parse_qs(self.post_data.decode('utf-8'))
1589 self.assertEqual(d['name'], ['http'])
1590 self.assertEqual(d['funcName'], ['test_output'])
1591 self.assertEqual(d['msg'], [msg])
1592
1593 self.server.stop(2.0)
1594 self.root_logger.removeHandler(self.h_hdlr)
1595 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001596
Christian Heimes180510d2008-03-03 19:15:45 +00001597class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001598
Christian Heimes180510d2008-03-03 19:15:45 +00001599 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001600
Christian Heimes180510d2008-03-03 19:15:45 +00001601 def setUp(self):
1602 """Create a dict to remember potentially destroyed objects."""
1603 BaseTest.setUp(self)
1604 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001605
Christian Heimes180510d2008-03-03 19:15:45 +00001606 def _watch_for_survival(self, *args):
1607 """Watch the given objects for survival, by creating weakrefs to
1608 them."""
1609 for obj in args:
1610 key = id(obj), repr(obj)
1611 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001612
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001613 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001614 """Assert that all objects watched for survival have survived."""
1615 # Trigger cycle breaking.
1616 gc.collect()
1617 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001618 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001619 if ref() is None:
1620 dead.append(repr_)
1621 if dead:
1622 self.fail("%d objects should have survived "
1623 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001624
Christian Heimes180510d2008-03-03 19:15:45 +00001625 def test_persistent_loggers(self):
1626 # Logger objects are persistent and retain their configuration, even
1627 # if visible references are destroyed.
1628 self.root_logger.setLevel(logging.INFO)
1629 foo = logging.getLogger("foo")
1630 self._watch_for_survival(foo)
1631 foo.setLevel(logging.DEBUG)
1632 self.root_logger.debug(self.next_message())
1633 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001634 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001635 ('foo', 'DEBUG', '2'),
1636 ])
1637 del foo
1638 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001639 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001640 # foo has retained its settings.
1641 bar = logging.getLogger("foo")
1642 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001643 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001644 ('foo', 'DEBUG', '2'),
1645 ('foo', 'DEBUG', '3'),
1646 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001647
Benjamin Petersonf91df042009-02-13 02:50:59 +00001648
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001649class EncodingTest(BaseTest):
1650 def test_encoding_plain_file(self):
1651 # In Python 2.x, a plain file object is treated as having no encoding.
1652 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001653 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1654 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001655 # the non-ascii data we write to the log.
1656 data = "foo\x80"
1657 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001658 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001659 log.addHandler(handler)
1660 try:
1661 # write non-ascii data to the log.
1662 log.warning(data)
1663 finally:
1664 log.removeHandler(handler)
1665 handler.close()
1666 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001667 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001668 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001669 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001670 finally:
1671 f.close()
1672 finally:
1673 if os.path.isfile(fn):
1674 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001675
Benjamin Petersonf91df042009-02-13 02:50:59 +00001676 def test_encoding_cyrillic_unicode(self):
1677 log = logging.getLogger("test")
1678 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1679 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1680 #Ensure it's written in a Cyrillic encoding
1681 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001682 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001683 stream = io.BytesIO()
1684 writer = writer_class(stream, 'strict')
1685 handler = logging.StreamHandler(writer)
1686 log.addHandler(handler)
1687 try:
1688 log.warning(message)
1689 finally:
1690 log.removeHandler(handler)
1691 handler.close()
1692 # check we wrote exactly those bytes, ignoring trailing \n etc
1693 s = stream.getvalue()
1694 #Compare against what the data should be when encoded in CP-1251
1695 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1696
1697
Georg Brandlf9734072008-12-07 15:30:06 +00001698class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001699
Georg Brandlf9734072008-12-07 15:30:06 +00001700 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001701 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001702 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001703 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001704 warnings.filterwarnings("always", category=UserWarning)
1705 stream = io.StringIO()
1706 h = logging.StreamHandler(stream)
1707 logger = logging.getLogger("py.warnings")
1708 logger.addHandler(h)
1709 warnings.warn("I'm warning you...")
1710 logger.removeHandler(h)
1711 s = stream.getvalue()
1712 h.close()
1713 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001714
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001715 #See if an explicit file uses the original implementation
1716 a_file = io.StringIO()
1717 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1718 a_file, "Dummy line")
1719 s = a_file.getvalue()
1720 a_file.close()
1721 self.assertEqual(s,
1722 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1723
1724 def test_warnings_no_handlers(self):
1725 with warnings.catch_warnings():
1726 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001727 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001728
1729 # confirm our assumption: no loggers are set
1730 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001731 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001732
1733 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001734 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001735 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001736
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001737
1738def formatFunc(format, datefmt=None):
1739 return logging.Formatter(format, datefmt)
1740
1741def handlerFunc():
1742 return logging.StreamHandler()
1743
1744class CustomHandler(logging.StreamHandler):
1745 pass
1746
1747class ConfigDictTest(BaseTest):
1748
1749 """Reading logging config from a dictionary."""
1750
1751 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1752
1753 # config0 is a standard configuration.
1754 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001755 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001756 'formatters': {
1757 'form1' : {
1758 'format' : '%(levelname)s ++ %(message)s',
1759 },
1760 },
1761 'handlers' : {
1762 'hand1' : {
1763 'class' : 'logging.StreamHandler',
1764 'formatter' : 'form1',
1765 'level' : 'NOTSET',
1766 'stream' : 'ext://sys.stdout',
1767 },
1768 },
1769 'root' : {
1770 'level' : 'WARNING',
1771 'handlers' : ['hand1'],
1772 },
1773 }
1774
1775 # config1 adds a little to the standard configuration.
1776 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001777 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001778 'formatters': {
1779 'form1' : {
1780 'format' : '%(levelname)s ++ %(message)s',
1781 },
1782 },
1783 'handlers' : {
1784 'hand1' : {
1785 'class' : 'logging.StreamHandler',
1786 'formatter' : 'form1',
1787 'level' : 'NOTSET',
1788 'stream' : 'ext://sys.stdout',
1789 },
1790 },
1791 'loggers' : {
1792 'compiler.parser' : {
1793 'level' : 'DEBUG',
1794 'handlers' : ['hand1'],
1795 },
1796 },
1797 'root' : {
1798 'level' : 'WARNING',
1799 },
1800 }
1801
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001802 # config1a moves the handler to the root. Used with config8a
1803 config1a = {
1804 'version': 1,
1805 'formatters': {
1806 'form1' : {
1807 'format' : '%(levelname)s ++ %(message)s',
1808 },
1809 },
1810 'handlers' : {
1811 'hand1' : {
1812 'class' : 'logging.StreamHandler',
1813 'formatter' : 'form1',
1814 'level' : 'NOTSET',
1815 'stream' : 'ext://sys.stdout',
1816 },
1817 },
1818 'loggers' : {
1819 'compiler.parser' : {
1820 'level' : 'DEBUG',
1821 },
1822 },
1823 'root' : {
1824 'level' : 'WARNING',
1825 'handlers' : ['hand1'],
1826 },
1827 }
1828
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001829 # config2 has a subtle configuration error that should be reported
1830 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001831 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001832 'formatters': {
1833 'form1' : {
1834 'format' : '%(levelname)s ++ %(message)s',
1835 },
1836 },
1837 'handlers' : {
1838 'hand1' : {
1839 'class' : 'logging.StreamHandler',
1840 'formatter' : 'form1',
1841 'level' : 'NOTSET',
1842 'stream' : 'ext://sys.stdbout',
1843 },
1844 },
1845 'loggers' : {
1846 'compiler.parser' : {
1847 'level' : 'DEBUG',
1848 'handlers' : ['hand1'],
1849 },
1850 },
1851 'root' : {
1852 'level' : 'WARNING',
1853 },
1854 }
1855
1856 #As config1 but with a misspelt level on a handler
1857 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001858 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001859 'formatters': {
1860 'form1' : {
1861 'format' : '%(levelname)s ++ %(message)s',
1862 },
1863 },
1864 'handlers' : {
1865 'hand1' : {
1866 'class' : 'logging.StreamHandler',
1867 'formatter' : 'form1',
1868 'level' : 'NTOSET',
1869 'stream' : 'ext://sys.stdout',
1870 },
1871 },
1872 'loggers' : {
1873 'compiler.parser' : {
1874 'level' : 'DEBUG',
1875 'handlers' : ['hand1'],
1876 },
1877 },
1878 'root' : {
1879 'level' : 'WARNING',
1880 },
1881 }
1882
1883
1884 #As config1 but with a misspelt level on a logger
1885 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001886 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001887 'formatters': {
1888 'form1' : {
1889 'format' : '%(levelname)s ++ %(message)s',
1890 },
1891 },
1892 'handlers' : {
1893 'hand1' : {
1894 'class' : 'logging.StreamHandler',
1895 'formatter' : 'form1',
1896 'level' : 'NOTSET',
1897 'stream' : 'ext://sys.stdout',
1898 },
1899 },
1900 'loggers' : {
1901 'compiler.parser' : {
1902 'level' : 'DEBUG',
1903 'handlers' : ['hand1'],
1904 },
1905 },
1906 'root' : {
1907 'level' : 'WRANING',
1908 },
1909 }
1910
1911 # config3 has a less subtle configuration error
1912 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001913 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001914 'formatters': {
1915 'form1' : {
1916 'format' : '%(levelname)s ++ %(message)s',
1917 },
1918 },
1919 'handlers' : {
1920 'hand1' : {
1921 'class' : 'logging.StreamHandler',
1922 'formatter' : 'misspelled_name',
1923 'level' : 'NOTSET',
1924 'stream' : 'ext://sys.stdout',
1925 },
1926 },
1927 'loggers' : {
1928 'compiler.parser' : {
1929 'level' : 'DEBUG',
1930 'handlers' : ['hand1'],
1931 },
1932 },
1933 'root' : {
1934 'level' : 'WARNING',
1935 },
1936 }
1937
1938 # config4 specifies a custom formatter class to be loaded
1939 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001940 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001941 'formatters': {
1942 'form1' : {
1943 '()' : __name__ + '.ExceptionFormatter',
1944 'format' : '%(levelname)s:%(name)s:%(message)s',
1945 },
1946 },
1947 'handlers' : {
1948 'hand1' : {
1949 'class' : 'logging.StreamHandler',
1950 'formatter' : 'form1',
1951 'level' : 'NOTSET',
1952 'stream' : 'ext://sys.stdout',
1953 },
1954 },
1955 'root' : {
1956 'level' : 'NOTSET',
1957 'handlers' : ['hand1'],
1958 },
1959 }
1960
1961 # As config4 but using an actual callable rather than a string
1962 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001963 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001964 'formatters': {
1965 'form1' : {
1966 '()' : ExceptionFormatter,
1967 'format' : '%(levelname)s:%(name)s:%(message)s',
1968 },
1969 'form2' : {
1970 '()' : __name__ + '.formatFunc',
1971 'format' : '%(levelname)s:%(name)s:%(message)s',
1972 },
1973 'form3' : {
1974 '()' : formatFunc,
1975 'format' : '%(levelname)s:%(name)s:%(message)s',
1976 },
1977 },
1978 'handlers' : {
1979 'hand1' : {
1980 'class' : 'logging.StreamHandler',
1981 'formatter' : 'form1',
1982 'level' : 'NOTSET',
1983 'stream' : 'ext://sys.stdout',
1984 },
1985 'hand2' : {
1986 '()' : handlerFunc,
1987 },
1988 },
1989 'root' : {
1990 'level' : 'NOTSET',
1991 'handlers' : ['hand1'],
1992 },
1993 }
1994
1995 # config5 specifies a custom handler class to be loaded
1996 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001997 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001998 'formatters': {
1999 'form1' : {
2000 'format' : '%(levelname)s ++ %(message)s',
2001 },
2002 },
2003 'handlers' : {
2004 'hand1' : {
2005 'class' : __name__ + '.CustomHandler',
2006 'formatter' : 'form1',
2007 'level' : 'NOTSET',
2008 'stream' : 'ext://sys.stdout',
2009 },
2010 },
2011 'loggers' : {
2012 'compiler.parser' : {
2013 'level' : 'DEBUG',
2014 'handlers' : ['hand1'],
2015 },
2016 },
2017 'root' : {
2018 'level' : 'WARNING',
2019 },
2020 }
2021
2022 # config6 specifies a custom handler class to be loaded
2023 # but has bad arguments
2024 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002025 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002026 'formatters': {
2027 'form1' : {
2028 'format' : '%(levelname)s ++ %(message)s',
2029 },
2030 },
2031 'handlers' : {
2032 'hand1' : {
2033 'class' : __name__ + '.CustomHandler',
2034 'formatter' : 'form1',
2035 'level' : 'NOTSET',
2036 'stream' : 'ext://sys.stdout',
2037 '9' : 'invalid parameter name',
2038 },
2039 },
2040 'loggers' : {
2041 'compiler.parser' : {
2042 'level' : 'DEBUG',
2043 'handlers' : ['hand1'],
2044 },
2045 },
2046 'root' : {
2047 'level' : 'WARNING',
2048 },
2049 }
2050
2051 #config 7 does not define compiler.parser but defines compiler.lexer
2052 #so compiler.parser should be disabled after applying it
2053 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002054 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002055 'formatters': {
2056 'form1' : {
2057 'format' : '%(levelname)s ++ %(message)s',
2058 },
2059 },
2060 'handlers' : {
2061 'hand1' : {
2062 'class' : 'logging.StreamHandler',
2063 'formatter' : 'form1',
2064 'level' : 'NOTSET',
2065 'stream' : 'ext://sys.stdout',
2066 },
2067 },
2068 'loggers' : {
2069 'compiler.lexer' : {
2070 'level' : 'DEBUG',
2071 'handlers' : ['hand1'],
2072 },
2073 },
2074 'root' : {
2075 'level' : 'WARNING',
2076 },
2077 }
2078
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002079 # config8 defines both compiler and compiler.lexer
2080 # so compiler.parser should not be disabled (since
2081 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002082 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002083 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002084 'disable_existing_loggers' : False,
2085 'formatters': {
2086 'form1' : {
2087 'format' : '%(levelname)s ++ %(message)s',
2088 },
2089 },
2090 'handlers' : {
2091 'hand1' : {
2092 'class' : 'logging.StreamHandler',
2093 'formatter' : 'form1',
2094 'level' : 'NOTSET',
2095 'stream' : 'ext://sys.stdout',
2096 },
2097 },
2098 'loggers' : {
2099 'compiler' : {
2100 'level' : 'DEBUG',
2101 'handlers' : ['hand1'],
2102 },
2103 'compiler.lexer' : {
2104 },
2105 },
2106 'root' : {
2107 'level' : 'WARNING',
2108 },
2109 }
2110
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002111 # config8a disables existing loggers
2112 config8a = {
2113 'version': 1,
2114 'disable_existing_loggers' : True,
2115 'formatters': {
2116 'form1' : {
2117 'format' : '%(levelname)s ++ %(message)s',
2118 },
2119 },
2120 'handlers' : {
2121 'hand1' : {
2122 'class' : 'logging.StreamHandler',
2123 'formatter' : 'form1',
2124 'level' : 'NOTSET',
2125 'stream' : 'ext://sys.stdout',
2126 },
2127 },
2128 'loggers' : {
2129 'compiler' : {
2130 'level' : 'DEBUG',
2131 'handlers' : ['hand1'],
2132 },
2133 'compiler.lexer' : {
2134 },
2135 },
2136 'root' : {
2137 'level' : 'WARNING',
2138 },
2139 }
2140
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002141 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002142 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002143 'formatters': {
2144 'form1' : {
2145 'format' : '%(levelname)s ++ %(message)s',
2146 },
2147 },
2148 'handlers' : {
2149 'hand1' : {
2150 'class' : 'logging.StreamHandler',
2151 'formatter' : 'form1',
2152 'level' : 'WARNING',
2153 'stream' : 'ext://sys.stdout',
2154 },
2155 },
2156 'loggers' : {
2157 'compiler.parser' : {
2158 'level' : 'WARNING',
2159 'handlers' : ['hand1'],
2160 },
2161 },
2162 'root' : {
2163 'level' : 'NOTSET',
2164 },
2165 }
2166
2167 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002168 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002169 'incremental' : True,
2170 'handlers' : {
2171 'hand1' : {
2172 'level' : 'WARNING',
2173 },
2174 },
2175 'loggers' : {
2176 'compiler.parser' : {
2177 'level' : 'INFO',
2178 },
2179 },
2180 }
2181
2182 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002183 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002184 'incremental' : True,
2185 'handlers' : {
2186 'hand1' : {
2187 'level' : 'INFO',
2188 },
2189 },
2190 'loggers' : {
2191 'compiler.parser' : {
2192 'level' : 'INFO',
2193 },
2194 },
2195 }
2196
2197 #As config1 but with a filter added
2198 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002199 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002200 'formatters': {
2201 'form1' : {
2202 'format' : '%(levelname)s ++ %(message)s',
2203 },
2204 },
2205 'filters' : {
2206 'filt1' : {
2207 'name' : 'compiler.parser',
2208 },
2209 },
2210 'handlers' : {
2211 'hand1' : {
2212 'class' : 'logging.StreamHandler',
2213 'formatter' : 'form1',
2214 'level' : 'NOTSET',
2215 'stream' : 'ext://sys.stdout',
2216 'filters' : ['filt1'],
2217 },
2218 },
2219 'loggers' : {
2220 'compiler.parser' : {
2221 'level' : 'DEBUG',
2222 'filters' : ['filt1'],
2223 },
2224 },
2225 'root' : {
2226 'level' : 'WARNING',
2227 'handlers' : ['hand1'],
2228 },
2229 }
2230
2231 #As config1 but using cfg:// references
2232 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002233 'version': 1,
2234 'true_formatters': {
2235 'form1' : {
2236 'format' : '%(levelname)s ++ %(message)s',
2237 },
2238 },
2239 'handler_configs': {
2240 'hand1' : {
2241 'class' : 'logging.StreamHandler',
2242 'formatter' : 'form1',
2243 'level' : 'NOTSET',
2244 'stream' : 'ext://sys.stdout',
2245 },
2246 },
2247 'formatters' : 'cfg://true_formatters',
2248 'handlers' : {
2249 'hand1' : 'cfg://handler_configs[hand1]',
2250 },
2251 'loggers' : {
2252 'compiler.parser' : {
2253 'level' : 'DEBUG',
2254 'handlers' : ['hand1'],
2255 },
2256 },
2257 'root' : {
2258 'level' : 'WARNING',
2259 },
2260 }
2261
2262 #As config11 but missing the version key
2263 config12 = {
2264 'true_formatters': {
2265 'form1' : {
2266 'format' : '%(levelname)s ++ %(message)s',
2267 },
2268 },
2269 'handler_configs': {
2270 'hand1' : {
2271 'class' : 'logging.StreamHandler',
2272 'formatter' : 'form1',
2273 'level' : 'NOTSET',
2274 'stream' : 'ext://sys.stdout',
2275 },
2276 },
2277 'formatters' : 'cfg://true_formatters',
2278 'handlers' : {
2279 'hand1' : 'cfg://handler_configs[hand1]',
2280 },
2281 'loggers' : {
2282 'compiler.parser' : {
2283 'level' : 'DEBUG',
2284 'handlers' : ['hand1'],
2285 },
2286 },
2287 'root' : {
2288 'level' : 'WARNING',
2289 },
2290 }
2291
2292 #As config11 but using an unsupported version
2293 config13 = {
2294 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002295 'true_formatters': {
2296 'form1' : {
2297 'format' : '%(levelname)s ++ %(message)s',
2298 },
2299 },
2300 'handler_configs': {
2301 'hand1' : {
2302 'class' : 'logging.StreamHandler',
2303 'formatter' : 'form1',
2304 'level' : 'NOTSET',
2305 'stream' : 'ext://sys.stdout',
2306 },
2307 },
2308 'formatters' : 'cfg://true_formatters',
2309 'handlers' : {
2310 'hand1' : 'cfg://handler_configs[hand1]',
2311 },
2312 'loggers' : {
2313 'compiler.parser' : {
2314 'level' : 'DEBUG',
2315 'handlers' : ['hand1'],
2316 },
2317 },
2318 'root' : {
2319 'level' : 'WARNING',
2320 },
2321 }
2322
2323 def apply_config(self, conf):
2324 logging.config.dictConfig(conf)
2325
2326 def test_config0_ok(self):
2327 # A simple config which overrides the default settings.
2328 with captured_stdout() as output:
2329 self.apply_config(self.config0)
2330 logger = logging.getLogger()
2331 # Won't output anything
2332 logger.info(self.next_message())
2333 # Outputs a message
2334 logger.error(self.next_message())
2335 self.assert_log_lines([
2336 ('ERROR', '2'),
2337 ], stream=output)
2338 # Original logger output is empty.
2339 self.assert_log_lines([])
2340
2341 def test_config1_ok(self, config=config1):
2342 # A config defining a sub-parser as well.
2343 with captured_stdout() as output:
2344 self.apply_config(config)
2345 logger = logging.getLogger("compiler.parser")
2346 # Both will output a message
2347 logger.info(self.next_message())
2348 logger.error(self.next_message())
2349 self.assert_log_lines([
2350 ('INFO', '1'),
2351 ('ERROR', '2'),
2352 ], stream=output)
2353 # Original logger output is empty.
2354 self.assert_log_lines([])
2355
2356 def test_config2_failure(self):
2357 # A simple config which overrides the default settings.
2358 self.assertRaises(Exception, self.apply_config, self.config2)
2359
2360 def test_config2a_failure(self):
2361 # A simple config which overrides the default settings.
2362 self.assertRaises(Exception, self.apply_config, self.config2a)
2363
2364 def test_config2b_failure(self):
2365 # A simple config which overrides the default settings.
2366 self.assertRaises(Exception, self.apply_config, self.config2b)
2367
2368 def test_config3_failure(self):
2369 # A simple config which overrides the default settings.
2370 self.assertRaises(Exception, self.apply_config, self.config3)
2371
2372 def test_config4_ok(self):
2373 # A config specifying a custom formatter class.
2374 with captured_stdout() as output:
2375 self.apply_config(self.config4)
2376 #logger = logging.getLogger()
2377 try:
2378 raise RuntimeError()
2379 except RuntimeError:
2380 logging.exception("just testing")
2381 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002382 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002383 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2384 # Original logger output is empty
2385 self.assert_log_lines([])
2386
2387 def test_config4a_ok(self):
2388 # A config specifying a custom formatter class.
2389 with captured_stdout() as output:
2390 self.apply_config(self.config4a)
2391 #logger = logging.getLogger()
2392 try:
2393 raise RuntimeError()
2394 except RuntimeError:
2395 logging.exception("just testing")
2396 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002397 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002398 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2399 # Original logger output is empty
2400 self.assert_log_lines([])
2401
2402 def test_config5_ok(self):
2403 self.test_config1_ok(config=self.config5)
2404
2405 def test_config6_failure(self):
2406 self.assertRaises(Exception, self.apply_config, self.config6)
2407
2408 def test_config7_ok(self):
2409 with captured_stdout() as output:
2410 self.apply_config(self.config1)
2411 logger = logging.getLogger("compiler.parser")
2412 # Both will output a message
2413 logger.info(self.next_message())
2414 logger.error(self.next_message())
2415 self.assert_log_lines([
2416 ('INFO', '1'),
2417 ('ERROR', '2'),
2418 ], stream=output)
2419 # Original logger output is empty.
2420 self.assert_log_lines([])
2421 with captured_stdout() as output:
2422 self.apply_config(self.config7)
2423 logger = logging.getLogger("compiler.parser")
2424 self.assertTrue(logger.disabled)
2425 logger = logging.getLogger("compiler.lexer")
2426 # Both will output a message
2427 logger.info(self.next_message())
2428 logger.error(self.next_message())
2429 self.assert_log_lines([
2430 ('INFO', '3'),
2431 ('ERROR', '4'),
2432 ], stream=output)
2433 # Original logger output is empty.
2434 self.assert_log_lines([])
2435
2436 #Same as test_config_7_ok but don't disable old loggers.
2437 def test_config_8_ok(self):
2438 with captured_stdout() as output:
2439 self.apply_config(self.config1)
2440 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002441 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002442 logger.info(self.next_message())
2443 logger.error(self.next_message())
2444 self.assert_log_lines([
2445 ('INFO', '1'),
2446 ('ERROR', '2'),
2447 ], stream=output)
2448 # Original logger output is empty.
2449 self.assert_log_lines([])
2450 with captured_stdout() as output:
2451 self.apply_config(self.config8)
2452 logger = logging.getLogger("compiler.parser")
2453 self.assertFalse(logger.disabled)
2454 # Both will output a message
2455 logger.info(self.next_message())
2456 logger.error(self.next_message())
2457 logger = logging.getLogger("compiler.lexer")
2458 # Both will output a message
2459 logger.info(self.next_message())
2460 logger.error(self.next_message())
2461 self.assert_log_lines([
2462 ('INFO', '3'),
2463 ('ERROR', '4'),
2464 ('INFO', '5'),
2465 ('ERROR', '6'),
2466 ], stream=output)
2467 # Original logger output is empty.
2468 self.assert_log_lines([])
2469
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002470 def test_config_8a_ok(self):
2471 with captured_stdout() as output:
2472 self.apply_config(self.config1a)
2473 logger = logging.getLogger("compiler.parser")
2474 # See issue #11424. compiler-hyphenated sorts
2475 # between compiler and compiler.xyz and this
2476 # was preventing compiler.xyz from being included
2477 # in the child loggers of compiler because of an
2478 # overzealous loop termination condition.
2479 hyphenated = logging.getLogger('compiler-hyphenated')
2480 # All will output a message
2481 logger.info(self.next_message())
2482 logger.error(self.next_message())
2483 hyphenated.critical(self.next_message())
2484 self.assert_log_lines([
2485 ('INFO', '1'),
2486 ('ERROR', '2'),
2487 ('CRITICAL', '3'),
2488 ], stream=output)
2489 # Original logger output is empty.
2490 self.assert_log_lines([])
2491 with captured_stdout() as output:
2492 self.apply_config(self.config8a)
2493 logger = logging.getLogger("compiler.parser")
2494 self.assertFalse(logger.disabled)
2495 # Both will output a message
2496 logger.info(self.next_message())
2497 logger.error(self.next_message())
2498 logger = logging.getLogger("compiler.lexer")
2499 # Both will output a message
2500 logger.info(self.next_message())
2501 logger.error(self.next_message())
2502 # Will not appear
2503 hyphenated.critical(self.next_message())
2504 self.assert_log_lines([
2505 ('INFO', '4'),
2506 ('ERROR', '5'),
2507 ('INFO', '6'),
2508 ('ERROR', '7'),
2509 ], stream=output)
2510 # Original logger output is empty.
2511 self.assert_log_lines([])
2512
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002513 def test_config_9_ok(self):
2514 with captured_stdout() as output:
2515 self.apply_config(self.config9)
2516 logger = logging.getLogger("compiler.parser")
2517 #Nothing will be output since both handler and logger are set to WARNING
2518 logger.info(self.next_message())
2519 self.assert_log_lines([], stream=output)
2520 self.apply_config(self.config9a)
2521 #Nothing will be output since both handler is still set to WARNING
2522 logger.info(self.next_message())
2523 self.assert_log_lines([], stream=output)
2524 self.apply_config(self.config9b)
2525 #Message should now be output
2526 logger.info(self.next_message())
2527 self.assert_log_lines([
2528 ('INFO', '3'),
2529 ], stream=output)
2530
2531 def test_config_10_ok(self):
2532 with captured_stdout() as output:
2533 self.apply_config(self.config10)
2534 logger = logging.getLogger("compiler.parser")
2535 logger.warning(self.next_message())
2536 logger = logging.getLogger('compiler')
2537 #Not output, because filtered
2538 logger.warning(self.next_message())
2539 logger = logging.getLogger('compiler.lexer')
2540 #Not output, because filtered
2541 logger.warning(self.next_message())
2542 logger = logging.getLogger("compiler.parser.codegen")
2543 #Output, as not filtered
2544 logger.error(self.next_message())
2545 self.assert_log_lines([
2546 ('WARNING', '1'),
2547 ('ERROR', '4'),
2548 ], stream=output)
2549
2550 def test_config11_ok(self):
2551 self.test_config1_ok(self.config11)
2552
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002553 def test_config12_failure(self):
2554 self.assertRaises(Exception, self.apply_config, self.config12)
2555
2556 def test_config13_failure(self):
2557 self.assertRaises(Exception, self.apply_config, self.config13)
2558
Victor Stinner45df8202010-04-28 22:31:17 +00002559 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002560 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002561 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002562 # Ask for a randomly assigned port (by using port 0)
2563 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002564 t.start()
2565 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002566 # Now get the port allocated
2567 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002568 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002569 try:
2570 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2571 sock.settimeout(2.0)
2572 sock.connect(('localhost', port))
2573
2574 slen = struct.pack('>L', len(text))
2575 s = slen + text
2576 sentsofar = 0
2577 left = len(s)
2578 while left > 0:
2579 sent = sock.send(s[sentsofar:])
2580 sentsofar += sent
2581 left -= sent
2582 sock.close()
2583 finally:
2584 t.ready.wait(2.0)
2585 logging.config.stopListening()
2586 t.join(2.0)
2587
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002588 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002589 def test_listen_config_10_ok(self):
2590 with captured_stdout() as output:
2591 self.setup_via_listener(json.dumps(self.config10))
2592 logger = logging.getLogger("compiler.parser")
2593 logger.warning(self.next_message())
2594 logger = logging.getLogger('compiler')
2595 #Not output, because filtered
2596 logger.warning(self.next_message())
2597 logger = logging.getLogger('compiler.lexer')
2598 #Not output, because filtered
2599 logger.warning(self.next_message())
2600 logger = logging.getLogger("compiler.parser.codegen")
2601 #Output, as not filtered
2602 logger.error(self.next_message())
2603 self.assert_log_lines([
2604 ('WARNING', '1'),
2605 ('ERROR', '4'),
2606 ], stream=output)
2607
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002608 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002609 def test_listen_config_1_ok(self):
2610 with captured_stdout() as output:
2611 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2612 logger = logging.getLogger("compiler.parser")
2613 # Both will output a message
2614 logger.info(self.next_message())
2615 logger.error(self.next_message())
2616 self.assert_log_lines([
2617 ('INFO', '1'),
2618 ('ERROR', '2'),
2619 ], stream=output)
2620 # Original logger output is empty.
2621 self.assert_log_lines([])
2622
Vinay Sajip373baef2011-04-26 20:05:24 +01002623 def test_baseconfig(self):
2624 d = {
2625 'atuple': (1, 2, 3),
2626 'alist': ['a', 'b', 'c'],
2627 'adict': {'d': 'e', 'f': 3 },
2628 'nest1': ('g', ('h', 'i'), 'j'),
2629 'nest2': ['k', ['l', 'm'], 'n'],
2630 'nest3': ['o', 'cfg://alist', 'p'],
2631 }
2632 bc = logging.config.BaseConfigurator(d)
2633 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2634 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2635 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2636 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2637 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2638 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2639 v = bc.convert('cfg://nest3')
2640 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2641 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2642 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2643 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002644
2645class ManagerTest(BaseTest):
2646 def test_manager_loggerclass(self):
2647 logged = []
2648
2649 class MyLogger(logging.Logger):
2650 def _log(self, level, msg, args, exc_info=None, extra=None):
2651 logged.append(msg)
2652
2653 man = logging.Manager(None)
2654 self.assertRaises(TypeError, man.setLoggerClass, int)
2655 man.setLoggerClass(MyLogger)
2656 logger = man.getLogger('test')
2657 logger.warning('should appear in logged')
2658 logging.warning('should not appear in logged')
2659
2660 self.assertEqual(logged, ['should appear in logged'])
2661
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002662 def test_set_log_record_factory(self):
2663 man = logging.Manager(None)
2664 expected = object()
2665 man.setLogRecordFactory(expected)
2666 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002667
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002668class ChildLoggerTest(BaseTest):
2669 def test_child_loggers(self):
2670 r = logging.getLogger()
2671 l1 = logging.getLogger('abc')
2672 l2 = logging.getLogger('def.ghi')
2673 c1 = r.getChild('xyz')
2674 c2 = r.getChild('uvw.xyz')
2675 self.assertTrue(c1 is logging.getLogger('xyz'))
2676 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2677 c1 = l1.getChild('def')
2678 c2 = c1.getChild('ghi')
2679 c3 = l1.getChild('def.ghi')
2680 self.assertTrue(c1 is logging.getLogger('abc.def'))
2681 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2682 self.assertTrue(c2 is c3)
2683
2684
Vinay Sajip6fac8172010-10-19 20:44:14 +00002685class DerivedLogRecord(logging.LogRecord):
2686 pass
2687
Vinay Sajip61561522010-12-03 11:50:38 +00002688class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002689
2690 def setUp(self):
2691 class CheckingFilter(logging.Filter):
2692 def __init__(self, cls):
2693 self.cls = cls
2694
2695 def filter(self, record):
2696 t = type(record)
2697 if t is not self.cls:
2698 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2699 self.cls)
2700 raise TypeError(msg)
2701 return True
2702
2703 BaseTest.setUp(self)
2704 self.filter = CheckingFilter(DerivedLogRecord)
2705 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002706 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002707
2708 def tearDown(self):
2709 self.root_logger.removeFilter(self.filter)
2710 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002711 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002712
2713 def test_logrecord_class(self):
2714 self.assertRaises(TypeError, self.root_logger.warning,
2715 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002716 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002717 self.root_logger.error(self.next_message())
2718 self.assert_log_lines([
2719 ('root', 'ERROR', '2'),
2720 ])
2721
2722
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002723class QueueHandlerTest(BaseTest):
2724 # Do not bother with a logger name group.
2725 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2726
2727 def setUp(self):
2728 BaseTest.setUp(self)
2729 self.queue = queue.Queue(-1)
2730 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2731 self.que_logger = logging.getLogger('que')
2732 self.que_logger.propagate = False
2733 self.que_logger.setLevel(logging.WARNING)
2734 self.que_logger.addHandler(self.que_hdlr)
2735
2736 def tearDown(self):
2737 self.que_hdlr.close()
2738 BaseTest.tearDown(self)
2739
2740 def test_queue_handler(self):
2741 self.que_logger.debug(self.next_message())
2742 self.assertRaises(queue.Empty, self.queue.get_nowait)
2743 self.que_logger.info(self.next_message())
2744 self.assertRaises(queue.Empty, self.queue.get_nowait)
2745 msg = self.next_message()
2746 self.que_logger.warning(msg)
2747 data = self.queue.get_nowait()
2748 self.assertTrue(isinstance(data, logging.LogRecord))
2749 self.assertEqual(data.name, self.que_logger.name)
2750 self.assertEqual((data.msg, data.args), (msg, None))
2751
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002752 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2753 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002754 def test_queue_listener(self):
2755 handler = TestHandler(Matcher())
2756 listener = logging.handlers.QueueListener(self.queue, handler)
2757 listener.start()
2758 try:
2759 self.que_logger.warning(self.next_message())
2760 self.que_logger.error(self.next_message())
2761 self.que_logger.critical(self.next_message())
2762 finally:
2763 listener.stop()
2764 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2765 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2766 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2767
Vinay Sajip37eb3382011-04-26 20:26:41 +01002768ZERO = datetime.timedelta(0)
2769
2770class UTC(datetime.tzinfo):
2771 def utcoffset(self, dt):
2772 return ZERO
2773
2774 dst = utcoffset
2775
2776 def tzname(self, dt):
2777 return 'UTC'
2778
2779utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002780
Vinay Sajipa39c5712010-10-25 13:57:39 +00002781class FormatterTest(unittest.TestCase):
2782 def setUp(self):
2783 self.common = {
2784 'name': 'formatter.test',
2785 'level': logging.DEBUG,
2786 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2787 'lineno': 42,
2788 'exc_info': None,
2789 'func': None,
2790 'msg': 'Message with %d %s',
2791 'args': (2, 'placeholders'),
2792 }
2793 self.variants = {
2794 }
2795
2796 def get_record(self, name=None):
2797 result = dict(self.common)
2798 if name is not None:
2799 result.update(self.variants[name])
2800 return logging.makeLogRecord(result)
2801
2802 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002803 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002804 r = self.get_record()
2805 f = logging.Formatter('${%(message)s}')
2806 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2807 f = logging.Formatter('%(random)s')
2808 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002809 self.assertFalse(f.usesTime())
2810 f = logging.Formatter('%(asctime)s')
2811 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002812 f = logging.Formatter('%(asctime)-15s')
2813 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002814 f = logging.Formatter('asctime')
2815 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002816
2817 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002818 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002819 r = self.get_record()
2820 f = logging.Formatter('$%{message}%$', style='{')
2821 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2822 f = logging.Formatter('{random}', style='{')
2823 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002824 self.assertFalse(f.usesTime())
2825 f = logging.Formatter('{asctime}', style='{')
2826 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002827 f = logging.Formatter('{asctime!s:15}', style='{')
2828 self.assertTrue(f.usesTime())
2829 f = logging.Formatter('{asctime:15}', style='{')
2830 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002831 f = logging.Formatter('asctime', style='{')
2832 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002833
2834 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002835 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002836 r = self.get_record()
2837 f = logging.Formatter('$message', style='$')
2838 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2839 f = logging.Formatter('$$%${message}%$$', style='$')
2840 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2841 f = logging.Formatter('${random}', style='$')
2842 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002843 self.assertFalse(f.usesTime())
2844 f = logging.Formatter('${asctime}', style='$')
2845 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002846 f = logging.Formatter('${asctime', style='$')
2847 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002848 f = logging.Formatter('$asctime', style='$')
2849 self.assertTrue(f.usesTime())
2850 f = logging.Formatter('asctime', style='$')
2851 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002852
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002853 def test_invalid_style(self):
2854 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2855
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002856 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002857 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002858 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002859 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002860 r.msecs = 123
2861 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002862 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002863 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2864 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002865 f.format(r)
2866 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2867
2868class TestBufferingFormatter(logging.BufferingFormatter):
2869 def formatHeader(self, records):
2870 return '[(%d)' % len(records)
2871
2872 def formatFooter(self, records):
2873 return '(%d)]' % len(records)
2874
2875class BufferingFormatterTest(unittest.TestCase):
2876 def setUp(self):
2877 self.records = [
2878 logging.makeLogRecord({'msg': 'one'}),
2879 logging.makeLogRecord({'msg': 'two'}),
2880 ]
2881
2882 def test_default(self):
2883 f = logging.BufferingFormatter()
2884 self.assertEqual('', f.format([]))
2885 self.assertEqual('onetwo', f.format(self.records))
2886
2887 def test_custom(self):
2888 f = TestBufferingFormatter()
2889 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2890 lf = logging.Formatter('<%(message)s>')
2891 f = TestBufferingFormatter(lf)
2892 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002893
2894class ExceptionTest(BaseTest):
2895 def test_formatting(self):
2896 r = self.root_logger
2897 h = RecordingHandler()
2898 r.addHandler(h)
2899 try:
2900 raise RuntimeError('deliberate mistake')
2901 except:
2902 logging.exception('failed', stack_info=True)
2903 r.removeHandler(h)
2904 h.close()
2905 r = h.records[0]
2906 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2907 'call last):\n'))
2908 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2909 'deliberate mistake'))
2910 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2911 'call last):\n'))
2912 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2913 'stack_info=True)'))
2914
2915
Vinay Sajip5a27d402010-12-10 11:42:57 +00002916class LastResortTest(BaseTest):
2917 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002918 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002919 root = self.root_logger
2920 root.removeHandler(self.root_hdlr)
2921 old_stderr = sys.stderr
2922 old_lastresort = logging.lastResort
2923 old_raise_exceptions = logging.raiseExceptions
2924 try:
2925 sys.stderr = sio = io.StringIO()
2926 root.warning('This is your final chance!')
2927 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2928 #No handlers and no last resort, so 'No handlers' message
2929 logging.lastResort = None
2930 sys.stderr = sio = io.StringIO()
2931 root.warning('This is your final chance!')
2932 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2933 # 'No handlers' message only printed once
2934 sys.stderr = sio = io.StringIO()
2935 root.warning('This is your final chance!')
2936 self.assertEqual(sio.getvalue(), '')
2937 root.manager.emittedNoHandlerWarning = False
2938 #If raiseExceptions is False, no message is printed
2939 logging.raiseExceptions = False
2940 sys.stderr = sio = io.StringIO()
2941 root.warning('This is your final chance!')
2942 self.assertEqual(sio.getvalue(), '')
2943 finally:
2944 sys.stderr = old_stderr
2945 root.addHandler(self.root_hdlr)
2946 logging.lastResort = old_lastresort
2947 logging.raiseExceptions = old_raise_exceptions
2948
2949
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002950class FakeHandler:
2951
2952 def __init__(self, identifier, called):
2953 for method in ('acquire', 'flush', 'close', 'release'):
2954 setattr(self, method, self.record_call(identifier, method, called))
2955
2956 def record_call(self, identifier, method_name, called):
2957 def inner():
2958 called.append('{} - {}'.format(identifier, method_name))
2959 return inner
2960
2961
2962class RecordingHandler(logging.NullHandler):
2963
2964 def __init__(self, *args, **kwargs):
2965 super(RecordingHandler, self).__init__(*args, **kwargs)
2966 self.records = []
2967
2968 def handle(self, record):
2969 """Keep track of all the emitted records."""
2970 self.records.append(record)
2971
2972
2973class ShutdownTest(BaseTest):
2974
Vinay Sajip5e66b162011-04-20 15:41:14 +01002975 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002976
2977 def setUp(self):
2978 super(ShutdownTest, self).setUp()
2979 self.called = []
2980
2981 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002982 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002983
2984 def raise_error(self, error):
2985 def inner():
2986 raise error()
2987 return inner
2988
2989 def test_no_failure(self):
2990 # create some fake handlers
2991 handler0 = FakeHandler(0, self.called)
2992 handler1 = FakeHandler(1, self.called)
2993 handler2 = FakeHandler(2, self.called)
2994
2995 # create live weakref to those handlers
2996 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2997
2998 logging.shutdown(handlerList=list(handlers))
2999
3000 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
3001 '1 - acquire', '1 - flush', '1 - close', '1 - release',
3002 '0 - acquire', '0 - flush', '0 - close', '0 - release']
3003 self.assertEqual(expected, self.called)
3004
3005 def _test_with_failure_in_method(self, method, error):
3006 handler = FakeHandler(0, self.called)
3007 setattr(handler, method, self.raise_error(error))
3008 handlers = [logging.weakref.ref(handler)]
3009
3010 logging.shutdown(handlerList=list(handlers))
3011
3012 self.assertEqual('0 - release', self.called[-1])
3013
3014 def test_with_ioerror_in_acquire(self):
3015 self._test_with_failure_in_method('acquire', IOError)
3016
3017 def test_with_ioerror_in_flush(self):
3018 self._test_with_failure_in_method('flush', IOError)
3019
3020 def test_with_ioerror_in_close(self):
3021 self._test_with_failure_in_method('close', IOError)
3022
3023 def test_with_valueerror_in_acquire(self):
3024 self._test_with_failure_in_method('acquire', ValueError)
3025
3026 def test_with_valueerror_in_flush(self):
3027 self._test_with_failure_in_method('flush', ValueError)
3028
3029 def test_with_valueerror_in_close(self):
3030 self._test_with_failure_in_method('close', ValueError)
3031
3032 def test_with_other_error_in_acquire_without_raise(self):
3033 logging.raiseExceptions = False
3034 self._test_with_failure_in_method('acquire', IndexError)
3035
3036 def test_with_other_error_in_flush_without_raise(self):
3037 logging.raiseExceptions = False
3038 self._test_with_failure_in_method('flush', IndexError)
3039
3040 def test_with_other_error_in_close_without_raise(self):
3041 logging.raiseExceptions = False
3042 self._test_with_failure_in_method('close', IndexError)
3043
3044 def test_with_other_error_in_acquire_with_raise(self):
3045 logging.raiseExceptions = True
3046 self.assertRaises(IndexError, self._test_with_failure_in_method,
3047 'acquire', IndexError)
3048
3049 def test_with_other_error_in_flush_with_raise(self):
3050 logging.raiseExceptions = True
3051 self.assertRaises(IndexError, self._test_with_failure_in_method,
3052 'flush', IndexError)
3053
3054 def test_with_other_error_in_close_with_raise(self):
3055 logging.raiseExceptions = True
3056 self.assertRaises(IndexError, self._test_with_failure_in_method,
3057 'close', IndexError)
3058
3059
3060class ModuleLevelMiscTest(BaseTest):
3061
Vinay Sajip5e66b162011-04-20 15:41:14 +01003062 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003063
3064 def test_disable(self):
3065 old_disable = logging.root.manager.disable
3066 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003067 self.assertEqual(old_disable, 0)
3068 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003069
3070 logging.disable(83)
3071 self.assertEqual(logging.root.manager.disable, 83)
3072
3073 def _test_log(self, method, level=None):
3074 called = []
3075 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003076 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003077
3078 recording = RecordingHandler()
3079 logging.root.addHandler(recording)
3080
3081 log_method = getattr(logging, method)
3082 if level is not None:
3083 log_method(level, "test me: %r", recording)
3084 else:
3085 log_method("test me: %r", recording)
3086
3087 self.assertEqual(len(recording.records), 1)
3088 record = recording.records[0]
3089 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3090
3091 expected_level = level if level is not None else getattr(logging, method.upper())
3092 self.assertEqual(record.levelno, expected_level)
3093
3094 # basicConfig was not called!
3095 self.assertEqual(called, [])
3096
3097 def test_log(self):
3098 self._test_log('log', logging.ERROR)
3099
3100 def test_debug(self):
3101 self._test_log('debug')
3102
3103 def test_info(self):
3104 self._test_log('info')
3105
3106 def test_warning(self):
3107 self._test_log('warning')
3108
3109 def test_error(self):
3110 self._test_log('error')
3111
3112 def test_critical(self):
3113 self._test_log('critical')
3114
3115 def test_set_logger_class(self):
3116 self.assertRaises(TypeError, logging.setLoggerClass, object)
3117
3118 class MyLogger(logging.Logger):
3119 pass
3120
3121 logging.setLoggerClass(MyLogger)
3122 self.assertEqual(logging.getLoggerClass(), MyLogger)
3123
3124 logging.setLoggerClass(logging.Logger)
3125 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3126
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003127class LogRecordTest(BaseTest):
3128 def test_str_rep(self):
3129 r = logging.makeLogRecord({})
3130 s = str(r)
3131 self.assertTrue(s.startswith('<LogRecord: '))
3132 self.assertTrue(s.endswith('>'))
3133
3134 def test_dict_arg(self):
3135 h = RecordingHandler()
3136 r = logging.getLogger()
3137 r.addHandler(h)
3138 d = {'less' : 'more' }
3139 logging.warning('less is %(less)s', d)
3140 self.assertIs(h.records[0].args, d)
3141 self.assertEqual(h.records[0].message, 'less is more')
3142 r.removeHandler(h)
3143 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003144
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003145 def test_multiprocessing(self):
3146 r = logging.makeLogRecord({})
3147 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003148 try:
3149 import multiprocessing as mp
3150 r = logging.makeLogRecord({})
3151 self.assertEqual(r.processName, mp.current_process().name)
3152 except ImportError:
3153 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003154
3155 def test_optional(self):
3156 r = logging.makeLogRecord({})
3157 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003158 if threading:
3159 NOT_NONE(r.thread)
3160 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003161 NOT_NONE(r.process)
3162 NOT_NONE(r.processName)
3163 log_threads = logging.logThreads
3164 log_processes = logging.logProcesses
3165 log_multiprocessing = logging.logMultiprocessing
3166 try:
3167 logging.logThreads = False
3168 logging.logProcesses = False
3169 logging.logMultiprocessing = False
3170 r = logging.makeLogRecord({})
3171 NONE = self.assertIsNone
3172 NONE(r.thread)
3173 NONE(r.threadName)
3174 NONE(r.process)
3175 NONE(r.processName)
3176 finally:
3177 logging.logThreads = log_threads
3178 logging.logProcesses = log_processes
3179 logging.logMultiprocessing = log_multiprocessing
3180
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003181class BasicConfigTest(unittest.TestCase):
3182
Vinay Sajip95bf5042011-04-20 11:50:56 +01003183 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003184
3185 def setUp(self):
3186 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003187 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003188 self.saved_handlers = logging._handlers.copy()
3189 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003190 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003191 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003192 logging.root.handlers = []
3193
3194 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003195 for h in logging.root.handlers[:]:
3196 logging.root.removeHandler(h)
3197 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003198 super(BasicConfigTest, self).tearDown()
3199
Vinay Sajip3def7e02011-04-20 10:58:06 +01003200 def cleanup(self):
3201 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003202 logging._handlers.clear()
3203 logging._handlers.update(self.saved_handlers)
3204 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003205 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003206
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003207 def test_no_kwargs(self):
3208 logging.basicConfig()
3209
3210 # handler defaults to a StreamHandler to sys.stderr
3211 self.assertEqual(len(logging.root.handlers), 1)
3212 handler = logging.root.handlers[0]
3213 self.assertIsInstance(handler, logging.StreamHandler)
3214 self.assertEqual(handler.stream, sys.stderr)
3215
3216 formatter = handler.formatter
3217 # format defaults to logging.BASIC_FORMAT
3218 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3219 # datefmt defaults to None
3220 self.assertIsNone(formatter.datefmt)
3221 # style defaults to %
3222 self.assertIsInstance(formatter._style, logging.PercentStyle)
3223
3224 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003225 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003226
3227 def test_filename(self):
3228 logging.basicConfig(filename='test.log')
3229
3230 self.assertEqual(len(logging.root.handlers), 1)
3231 handler = logging.root.handlers[0]
3232 self.assertIsInstance(handler, logging.FileHandler)
3233
3234 expected = logging.FileHandler('test.log', 'a')
3235 self.addCleanup(expected.close)
3236 self.assertEqual(handler.stream.mode, expected.stream.mode)
3237 self.assertEqual(handler.stream.name, expected.stream.name)
3238
3239 def test_filemode(self):
3240 logging.basicConfig(filename='test.log', filemode='wb')
3241
3242 handler = logging.root.handlers[0]
3243 expected = logging.FileHandler('test.log', 'wb')
3244 self.addCleanup(expected.close)
3245 self.assertEqual(handler.stream.mode, expected.stream.mode)
3246
3247 def test_stream(self):
3248 stream = io.StringIO()
3249 self.addCleanup(stream.close)
3250 logging.basicConfig(stream=stream)
3251
3252 self.assertEqual(len(logging.root.handlers), 1)
3253 handler = logging.root.handlers[0]
3254 self.assertIsInstance(handler, logging.StreamHandler)
3255 self.assertEqual(handler.stream, stream)
3256
3257 def test_format(self):
3258 logging.basicConfig(format='foo')
3259
3260 formatter = logging.root.handlers[0].formatter
3261 self.assertEqual(formatter._style._fmt, 'foo')
3262
3263 def test_datefmt(self):
3264 logging.basicConfig(datefmt='bar')
3265
3266 formatter = logging.root.handlers[0].formatter
3267 self.assertEqual(formatter.datefmt, 'bar')
3268
3269 def test_style(self):
3270 logging.basicConfig(style='$')
3271
3272 formatter = logging.root.handlers[0].formatter
3273 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3274
3275 def test_level(self):
3276 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003277 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003278
3279 logging.basicConfig(level=57)
3280 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003281 # Test that second call has no effect
3282 logging.basicConfig(level=58)
3283 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003284
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003285 def test_incompatible(self):
3286 assertRaises = self.assertRaises
3287 handlers = [logging.StreamHandler()]
3288 stream = sys.stderr
3289 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3290 stream=stream)
3291 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3292 handlers=handlers)
3293 assertRaises(ValueError, logging.basicConfig, stream=stream,
3294 handlers=handlers)
3295
3296 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003297 handlers = [
3298 logging.StreamHandler(),
3299 logging.StreamHandler(sys.stdout),
3300 logging.StreamHandler(),
3301 ]
3302 f = logging.Formatter()
3303 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003304 logging.basicConfig(handlers=handlers)
3305 self.assertIs(handlers[0], logging.root.handlers[0])
3306 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003307 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003308 self.assertIsNotNone(handlers[0].formatter)
3309 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003310 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003311 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3312
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003313 def _test_log(self, method, level=None):
3314 # logging.root has no handlers so basicConfig should be called
3315 called = []
3316
3317 old_basic_config = logging.basicConfig
3318 def my_basic_config(*a, **kw):
3319 old_basic_config()
3320 old_level = logging.root.level
3321 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003322 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003323 called.append((a, kw))
3324
3325 patch(self, logging, 'basicConfig', my_basic_config)
3326
3327 log_method = getattr(logging, method)
3328 if level is not None:
3329 log_method(level, "test me")
3330 else:
3331 log_method("test me")
3332
3333 # basicConfig was called with no arguments
3334 self.assertEqual(called, [((), {})])
3335
3336 def test_log(self):
3337 self._test_log('log', logging.WARNING)
3338
3339 def test_debug(self):
3340 self._test_log('debug')
3341
3342 def test_info(self):
3343 self._test_log('info')
3344
3345 def test_warning(self):
3346 self._test_log('warning')
3347
3348 def test_error(self):
3349 self._test_log('error')
3350
3351 def test_critical(self):
3352 self._test_log('critical')
3353
3354
3355class LoggerAdapterTest(unittest.TestCase):
3356
3357 def setUp(self):
3358 super(LoggerAdapterTest, self).setUp()
3359 old_handler_list = logging._handlerList[:]
3360
3361 self.recording = RecordingHandler()
3362 self.logger = logging.root
3363 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003364 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003365 self.addCleanup(self.recording.close)
3366
3367 def cleanup():
3368 logging._handlerList[:] = old_handler_list
3369
3370 self.addCleanup(cleanup)
3371 self.addCleanup(logging.shutdown)
3372 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3373
3374 def test_exception(self):
3375 msg = 'testing exception: %r'
3376 exc = None
3377 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003378 1 / 0
3379 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003380 exc = e
3381 self.adapter.exception(msg, self.recording)
3382
3383 self.assertEqual(len(self.recording.records), 1)
3384 record = self.recording.records[0]
3385 self.assertEqual(record.levelno, logging.ERROR)
3386 self.assertEqual(record.msg, msg)
3387 self.assertEqual(record.args, (self.recording,))
3388 self.assertEqual(record.exc_info,
3389 (exc.__class__, exc, exc.__traceback__))
3390
3391 def test_critical(self):
3392 msg = 'critical test! %r'
3393 self.adapter.critical(msg, self.recording)
3394
3395 self.assertEqual(len(self.recording.records), 1)
3396 record = self.recording.records[0]
3397 self.assertEqual(record.levelno, logging.CRITICAL)
3398 self.assertEqual(record.msg, msg)
3399 self.assertEqual(record.args, (self.recording,))
3400
3401 def test_is_enabled_for(self):
3402 old_disable = self.adapter.logger.manager.disable
3403 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003404 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3405 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003406 self.assertFalse(self.adapter.isEnabledFor(32))
3407
3408 def test_has_handlers(self):
3409 self.assertTrue(self.adapter.hasHandlers())
3410
3411 for handler in self.logger.handlers:
3412 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003413
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003414 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003415 self.assertFalse(self.adapter.hasHandlers())
3416
3417
3418class LoggerTest(BaseTest):
3419
3420 def setUp(self):
3421 super(LoggerTest, self).setUp()
3422 self.recording = RecordingHandler()
3423 self.logger = logging.Logger(name='blah')
3424 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003425 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003426 self.addCleanup(self.recording.close)
3427 self.addCleanup(logging.shutdown)
3428
3429 def test_set_invalid_level(self):
3430 self.assertRaises(TypeError, self.logger.setLevel, object())
3431
3432 def test_exception(self):
3433 msg = 'testing exception: %r'
3434 exc = None
3435 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003436 1 / 0
3437 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003438 exc = e
3439 self.logger.exception(msg, self.recording)
3440
3441 self.assertEqual(len(self.recording.records), 1)
3442 record = self.recording.records[0]
3443 self.assertEqual(record.levelno, logging.ERROR)
3444 self.assertEqual(record.msg, msg)
3445 self.assertEqual(record.args, (self.recording,))
3446 self.assertEqual(record.exc_info,
3447 (exc.__class__, exc, exc.__traceback__))
3448
3449 def test_log_invalid_level_with_raise(self):
3450 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003451 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003452
3453 logging.raiseExceptions = True
3454 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3455
3456 def test_log_invalid_level_no_raise(self):
3457 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003458 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003459
3460 logging.raiseExceptions = False
3461 self.logger.log('10', 'test message') # no exception happens
3462
3463 def test_find_caller_with_stack_info(self):
3464 called = []
3465 patch(self, logging.traceback, 'print_stack',
3466 lambda f, file: called.append(file.getvalue()))
3467
3468 self.logger.findCaller(stack_info=True)
3469
3470 self.assertEqual(len(called), 1)
3471 self.assertEqual('Stack (most recent call last):\n', called[0])
3472
3473 def test_make_record_with_extra_overwrite(self):
3474 name = 'my record'
3475 level = 13
3476 fn = lno = msg = args = exc_info = func = sinfo = None
3477 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3478 exc_info, func, sinfo)
3479
3480 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3481 extra = {key: 'some value'}
3482 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3483 fn, lno, msg, args, exc_info,
3484 extra=extra, sinfo=sinfo)
3485
3486 def test_make_record_with_extra_no_overwrite(self):
3487 name = 'my record'
3488 level = 13
3489 fn = lno = msg = args = exc_info = func = sinfo = None
3490 extra = {'valid_key': 'some value'}
3491 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3492 exc_info, extra=extra, sinfo=sinfo)
3493 self.assertIn('valid_key', result.__dict__)
3494
3495 def test_has_handlers(self):
3496 self.assertTrue(self.logger.hasHandlers())
3497
3498 for handler in self.logger.handlers:
3499 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003500 self.assertFalse(self.logger.hasHandlers())
3501
3502 def test_has_handlers_no_propagate(self):
3503 child_logger = logging.getLogger('blah.child')
3504 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003505 self.assertFalse(child_logger.hasHandlers())
3506
3507 def test_is_enabled_for(self):
3508 old_disable = self.logger.manager.disable
3509 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003510 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003511 self.assertFalse(self.logger.isEnabledFor(22))
3512
3513
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003514class BaseFileTest(BaseTest):
3515 "Base class for handler tests that write log files"
3516
3517 def setUp(self):
3518 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003519 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3520 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003521 self.rmfiles = []
3522
3523 def tearDown(self):
3524 for fn in self.rmfiles:
3525 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003526 if os.path.exists(self.fn):
3527 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003528 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003529
3530 def assertLogFile(self, filename):
3531 "Assert a log file is there and register it for deletion"
3532 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003533 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003534 self.rmfiles.append(filename)
3535
3536
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003537class FileHandlerTest(BaseFileTest):
3538 def test_delay(self):
3539 os.unlink(self.fn)
3540 fh = logging.FileHandler(self.fn, delay=True)
3541 self.assertIsNone(fh.stream)
3542 self.assertFalse(os.path.exists(self.fn))
3543 fh.handle(logging.makeLogRecord({}))
3544 self.assertIsNotNone(fh.stream)
3545 self.assertTrue(os.path.exists(self.fn))
3546 fh.close()
3547
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003548class RotatingFileHandlerTest(BaseFileTest):
3549 def next_rec(self):
3550 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3551 self.next_message(), None, None, None)
3552
3553 def test_should_not_rollover(self):
3554 # If maxbytes is zero rollover never occurs
3555 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3556 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003557 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003558
3559 def test_should_rollover(self):
3560 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3561 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003562 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003563
3564 def test_file_created(self):
3565 # checks that the file is created and assumes it was created
3566 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003567 rh = logging.handlers.RotatingFileHandler(self.fn)
3568 rh.emit(self.next_rec())
3569 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003570 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003571
3572 def test_rollover_filenames(self):
3573 rh = logging.handlers.RotatingFileHandler(
3574 self.fn, backupCount=2, maxBytes=1)
3575 rh.emit(self.next_rec())
3576 self.assertLogFile(self.fn)
3577 rh.emit(self.next_rec())
3578 self.assertLogFile(self.fn + ".1")
3579 rh.emit(self.next_rec())
3580 self.assertLogFile(self.fn + ".2")
3581 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003582 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003583
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003584class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003585 # other test methods added below
3586 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003587 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3588 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003589 r = logging.makeLogRecord({'msg': 'testing'})
3590 fh.emit(r)
3591 self.assertLogFile(self.fn)
Vinay Sajipb7b19302011-05-24 07:12:41 +01003592 time.sleep(1.01) # just a little over a second ...
Vinay Sajipfd285022011-05-07 17:01:22 +01003593 fh.emit(r)
3594 fh.close()
3595 # At this point, we should have a recent rotated file which we
3596 # can test for the existence of. However, in practice, on some
3597 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003598 # in time to go to look for the log file. So, we go back a fair
3599 # bit, and stop as soon as we see a rotated file. In theory this
3600 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003601 found = False
3602 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003603 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003604 for secs in range(GO_BACK):
3605 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003606 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3607 found = os.path.exists(fn)
3608 if found:
3609 self.rmfiles.append(fn)
3610 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003611 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3612 if not found:
3613 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003614 dn, fn = os.path.split(self.fn)
3615 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003616 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3617 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajipfd285022011-05-07 17:01:22 +01003618 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003619
Vinay Sajip0372e102011-05-05 12:59:14 +01003620 def test_invalid(self):
3621 assertRaises = self.assertRaises
3622 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003623 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003624 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003625 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003626 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003627 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003628
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003629def secs(**kw):
3630 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3631
3632for when, exp in (('S', 1),
3633 ('M', 60),
3634 ('H', 60 * 60),
3635 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003636 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003637 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003638 ('W0', secs(days=4, hours=24)),
3639 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003640 def test_compute_rollover(self, when=when, exp=exp):
3641 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003642 self.fn, when=when, interval=1, backupCount=0, utc=True)
3643 currentTime = 0.0
3644 actual = rh.computeRollover(currentTime)
3645 if exp != actual:
3646 # Failures occur on some systems for MIDNIGHT and W0.
3647 # Print detailed calculation for MIDNIGHT so we can try to see
3648 # what's going on
3649 import time
3650 if when == 'MIDNIGHT':
3651 try:
3652 if rh.utc:
3653 t = time.gmtime(currentTime)
3654 else:
3655 t = time.localtime(currentTime)
3656 currentHour = t[3]
3657 currentMinute = t[4]
3658 currentSecond = t[5]
3659 # r is the number of seconds left between now and midnight
3660 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3661 currentMinute) * 60 +
3662 currentSecond)
3663 result = currentTime + r
3664 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3665 print('currentHour: %s' % currentHour, file=sys.stderr)
3666 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3667 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3668 print('r: %s' % r, file=sys.stderr)
3669 print('result: %s' % result, file=sys.stderr)
3670 except Exception:
3671 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3672 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003673 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003674 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3675
Vinay Sajip60ccd822011-05-09 17:32:09 +01003676
3677@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3678class NTEventLogHandlerTest(BaseTest):
3679 def test_basic(self):
3680 logtype = 'Application'
3681 elh = win32evtlog.OpenEventLog(None, logtype)
3682 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3683 h = logging.handlers.NTEventLogHandler('test_logging')
3684 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3685 h.handle(r)
3686 h.close()
3687 # Now see if the event is recorded
3688 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3689 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3690 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3691 found = False
3692 GO_BACK = 100
3693 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3694 for e in events:
3695 if e.SourceName != 'test_logging':
3696 continue
3697 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3698 if msg != 'Test Log Message\r\n':
3699 continue
3700 found = True
3701 break
3702 msg = 'Record not found in event log, went back %d records' % GO_BACK
3703 self.assertTrue(found, msg=msg)
3704
Christian Heimes180510d2008-03-03 19:15:45 +00003705# Set the locale to the platform-dependent default. I have no idea
3706# why the test does this, but in any case we save the current locale
3707# first and restore it at the end.
3708@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003709def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003710 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003711 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003712 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3713 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3714 ManagerTest, FormatterTest, BufferingFormatterTest,
3715 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3716 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3717 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3718 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003719 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003720 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003721 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003722 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003723
Christian Heimes180510d2008-03-03 19:15:45 +00003724if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003725 test_main()