blob: ceefd9508d8a0d024db15b0bf0a9229394bfeaa4 [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)
1402 self.server = server = TestUDPServer(addr, self.handle_datagram,
1403 0.01)
1404 server.start()
1405 server.ready.wait()
1406 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1407 server.port)
1408 self.log_output = ''
1409 self.root_logger.removeHandler(self.root_logger.handlers[0])
1410 self.root_logger.addHandler(self.sock_hdlr)
1411 self.handled = threading.Event()
1412
1413 def tearDown(self):
1414 """Shutdown the UDP server."""
1415 try:
1416 self.server.stop(2.0)
1417 self.root_logger.removeHandler(self.sock_hdlr)
1418 self.sock_hdlr.close()
1419 finally:
1420 BaseTest.tearDown(self)
1421
1422 def handle_datagram(self, request):
1423 slen = struct.pack('>L', 0) # length of prefix
1424 packet = request.packet[len(slen):]
1425 obj = pickle.loads(packet)
1426 record = logging.makeLogRecord(obj)
1427 self.log_output += record.msg + '\n'
1428 self.handled.set()
1429
1430 def test_output(self):
1431 # The log message sent to the DatagramHandler is properly received.
1432 logger = logging.getLogger("udp")
1433 logger.error("spam")
1434 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001435 self.handled.clear()
1436 logger.error("eggs")
1437 self.handled.wait()
1438 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001439
1440
1441@unittest.skipUnless(threading, 'Threading required for this test.')
1442class SysLogHandlerTest(BaseTest):
1443
1444 """Test for SysLogHandler using UDP."""
1445
1446 def setUp(self):
1447 """Set up a UDP server to receive log messages, and a SysLogHandler
1448 pointing to that server's address and port."""
1449 BaseTest.setUp(self)
1450 addr = ('localhost', 0)
1451 self.server = server = TestUDPServer(addr, self.handle_datagram,
1452 0.01)
1453 server.start()
1454 server.ready.wait()
1455 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1456 server.port))
1457 self.log_output = ''
1458 self.root_logger.removeHandler(self.root_logger.handlers[0])
1459 self.root_logger.addHandler(self.sl_hdlr)
1460 self.handled = threading.Event()
1461
1462 def tearDown(self):
1463 """Shutdown the UDP server."""
1464 try:
1465 self.server.stop(2.0)
1466 self.root_logger.removeHandler(self.sl_hdlr)
1467 self.sl_hdlr.close()
1468 finally:
1469 BaseTest.tearDown(self)
1470
1471 def handle_datagram(self, request):
1472 self.log_output = request.packet
1473 self.handled.set()
1474
1475 def test_output(self):
1476 # The log message sent to the SysLogHandler is properly received.
1477 logger = logging.getLogger("slh")
1478 logger.error("sp\xe4m")
1479 self.handled.wait()
1480 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1481
1482
1483@unittest.skipUnless(threading, 'Threading required for this test.')
1484class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001485 """Test for HTTPHandler."""
1486
Vinay Sajip9ba87612011-05-21 11:32:15 +01001487 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1488MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1489zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1490aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1491AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
14920tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
14935LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1494piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
14952z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1496i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1497PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1498E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1499rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1500/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1501-----END RSA PRIVATE KEY-----
1502-----BEGIN CERTIFICATE-----
1503MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1504BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1505EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1506xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1507l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1508vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1509BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1510S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1511BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15124f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15131c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1514IRbTpvlHWPjsSvHz0ZOH
1515-----END CERTIFICATE-----"""
1516
Vinay Sajip7367d082011-05-02 13:17:27 +01001517 def setUp(self):
1518 """Set up an HTTP server to receive log messages, and a HTTPHandler
1519 pointing to that server's address and port."""
1520 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001521 self.handled = threading.Event()
1522
Vinay Sajip7367d082011-05-02 13:17:27 +01001523 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001524 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001525 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001526 if self.command == 'POST':
1527 try:
1528 rlen = int(request.headers['Content-Length'])
1529 self.post_data = request.rfile.read(rlen)
1530 except:
1531 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001532 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001533 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001534 self.handled.set()
1535
1536 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001537 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001538 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001539 root_logger = self.root_logger
1540 root_logger.removeHandler(self.root_logger.handlers[0])
1541 for secure in (False, True):
1542 addr = ('localhost', 0)
1543 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001544 try:
1545 import ssl
1546 fd, fn = tempfile.mkstemp()
1547 os.close(fd)
1548 with open(fn, 'w') as f:
1549 f.write(self.PEMFILE)
1550 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1551 sslctx.load_cert_chain(fn)
1552 os.unlink(fn)
1553 except ImportError:
1554 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001555 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001556 sslctx = None
1557 self.server = server = TestHTTPServer(addr, self.handle_request,
1558 0.01, sslctx=sslctx)
1559 server.start()
1560 server.ready.wait()
1561 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001562 secure_client = secure and sslctx
1563 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1564 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001565 self.log_data = None
1566 root_logger.addHandler(self.h_hdlr)
1567
1568 for method in ('GET', 'POST'):
1569 self.h_hdlr.method = method
1570 self.handled.clear()
1571 msg = "sp\xe4m"
1572 logger.error(msg)
1573 self.handled.wait()
1574 self.assertEqual(self.log_data.path, '/frob')
1575 self.assertEqual(self.command, method)
1576 if method == 'GET':
1577 d = parse_qs(self.log_data.query)
1578 else:
1579 d = parse_qs(self.post_data.decode('utf-8'))
1580 self.assertEqual(d['name'], ['http'])
1581 self.assertEqual(d['funcName'], ['test_output'])
1582 self.assertEqual(d['msg'], [msg])
1583
1584 self.server.stop(2.0)
1585 self.root_logger.removeHandler(self.h_hdlr)
1586 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001587
Christian Heimes180510d2008-03-03 19:15:45 +00001588class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001589
Christian Heimes180510d2008-03-03 19:15:45 +00001590 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001591
Christian Heimes180510d2008-03-03 19:15:45 +00001592 def setUp(self):
1593 """Create a dict to remember potentially destroyed objects."""
1594 BaseTest.setUp(self)
1595 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001596
Christian Heimes180510d2008-03-03 19:15:45 +00001597 def _watch_for_survival(self, *args):
1598 """Watch the given objects for survival, by creating weakrefs to
1599 them."""
1600 for obj in args:
1601 key = id(obj), repr(obj)
1602 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001603
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001604 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001605 """Assert that all objects watched for survival have survived."""
1606 # Trigger cycle breaking.
1607 gc.collect()
1608 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001609 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001610 if ref() is None:
1611 dead.append(repr_)
1612 if dead:
1613 self.fail("%d objects should have survived "
1614 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001615
Christian Heimes180510d2008-03-03 19:15:45 +00001616 def test_persistent_loggers(self):
1617 # Logger objects are persistent and retain their configuration, even
1618 # if visible references are destroyed.
1619 self.root_logger.setLevel(logging.INFO)
1620 foo = logging.getLogger("foo")
1621 self._watch_for_survival(foo)
1622 foo.setLevel(logging.DEBUG)
1623 self.root_logger.debug(self.next_message())
1624 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001625 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001626 ('foo', 'DEBUG', '2'),
1627 ])
1628 del foo
1629 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001630 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001631 # foo has retained its settings.
1632 bar = logging.getLogger("foo")
1633 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001634 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001635 ('foo', 'DEBUG', '2'),
1636 ('foo', 'DEBUG', '3'),
1637 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001638
Benjamin Petersonf91df042009-02-13 02:50:59 +00001639
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001640class EncodingTest(BaseTest):
1641 def test_encoding_plain_file(self):
1642 # In Python 2.x, a plain file object is treated as having no encoding.
1643 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001644 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1645 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001646 # the non-ascii data we write to the log.
1647 data = "foo\x80"
1648 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001649 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001650 log.addHandler(handler)
1651 try:
1652 # write non-ascii data to the log.
1653 log.warning(data)
1654 finally:
1655 log.removeHandler(handler)
1656 handler.close()
1657 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001658 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001659 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001660 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001661 finally:
1662 f.close()
1663 finally:
1664 if os.path.isfile(fn):
1665 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001666
Benjamin Petersonf91df042009-02-13 02:50:59 +00001667 def test_encoding_cyrillic_unicode(self):
1668 log = logging.getLogger("test")
1669 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1670 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1671 #Ensure it's written in a Cyrillic encoding
1672 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001673 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001674 stream = io.BytesIO()
1675 writer = writer_class(stream, 'strict')
1676 handler = logging.StreamHandler(writer)
1677 log.addHandler(handler)
1678 try:
1679 log.warning(message)
1680 finally:
1681 log.removeHandler(handler)
1682 handler.close()
1683 # check we wrote exactly those bytes, ignoring trailing \n etc
1684 s = stream.getvalue()
1685 #Compare against what the data should be when encoded in CP-1251
1686 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1687
1688
Georg Brandlf9734072008-12-07 15:30:06 +00001689class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001690
Georg Brandlf9734072008-12-07 15:30:06 +00001691 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001692 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001693 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001694 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001695 warnings.filterwarnings("always", category=UserWarning)
1696 stream = io.StringIO()
1697 h = logging.StreamHandler(stream)
1698 logger = logging.getLogger("py.warnings")
1699 logger.addHandler(h)
1700 warnings.warn("I'm warning you...")
1701 logger.removeHandler(h)
1702 s = stream.getvalue()
1703 h.close()
1704 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001705
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001706 #See if an explicit file uses the original implementation
1707 a_file = io.StringIO()
1708 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1709 a_file, "Dummy line")
1710 s = a_file.getvalue()
1711 a_file.close()
1712 self.assertEqual(s,
1713 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1714
1715 def test_warnings_no_handlers(self):
1716 with warnings.catch_warnings():
1717 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001718 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001719
1720 # confirm our assumption: no loggers are set
1721 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001722 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001723
1724 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001725 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001726 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001727
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001728
1729def formatFunc(format, datefmt=None):
1730 return logging.Formatter(format, datefmt)
1731
1732def handlerFunc():
1733 return logging.StreamHandler()
1734
1735class CustomHandler(logging.StreamHandler):
1736 pass
1737
1738class ConfigDictTest(BaseTest):
1739
1740 """Reading logging config from a dictionary."""
1741
1742 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1743
1744 # config0 is a standard configuration.
1745 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001746 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001747 'formatters': {
1748 'form1' : {
1749 'format' : '%(levelname)s ++ %(message)s',
1750 },
1751 },
1752 'handlers' : {
1753 'hand1' : {
1754 'class' : 'logging.StreamHandler',
1755 'formatter' : 'form1',
1756 'level' : 'NOTSET',
1757 'stream' : 'ext://sys.stdout',
1758 },
1759 },
1760 'root' : {
1761 'level' : 'WARNING',
1762 'handlers' : ['hand1'],
1763 },
1764 }
1765
1766 # config1 adds a little to the standard configuration.
1767 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001768 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001769 'formatters': {
1770 'form1' : {
1771 'format' : '%(levelname)s ++ %(message)s',
1772 },
1773 },
1774 'handlers' : {
1775 'hand1' : {
1776 'class' : 'logging.StreamHandler',
1777 'formatter' : 'form1',
1778 'level' : 'NOTSET',
1779 'stream' : 'ext://sys.stdout',
1780 },
1781 },
1782 'loggers' : {
1783 'compiler.parser' : {
1784 'level' : 'DEBUG',
1785 'handlers' : ['hand1'],
1786 },
1787 },
1788 'root' : {
1789 'level' : 'WARNING',
1790 },
1791 }
1792
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001793 # config1a moves the handler to the root. Used with config8a
1794 config1a = {
1795 'version': 1,
1796 'formatters': {
1797 'form1' : {
1798 'format' : '%(levelname)s ++ %(message)s',
1799 },
1800 },
1801 'handlers' : {
1802 'hand1' : {
1803 'class' : 'logging.StreamHandler',
1804 'formatter' : 'form1',
1805 'level' : 'NOTSET',
1806 'stream' : 'ext://sys.stdout',
1807 },
1808 },
1809 'loggers' : {
1810 'compiler.parser' : {
1811 'level' : 'DEBUG',
1812 },
1813 },
1814 'root' : {
1815 'level' : 'WARNING',
1816 'handlers' : ['hand1'],
1817 },
1818 }
1819
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001820 # config2 has a subtle configuration error that should be reported
1821 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001822 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001823 'formatters': {
1824 'form1' : {
1825 'format' : '%(levelname)s ++ %(message)s',
1826 },
1827 },
1828 'handlers' : {
1829 'hand1' : {
1830 'class' : 'logging.StreamHandler',
1831 'formatter' : 'form1',
1832 'level' : 'NOTSET',
1833 'stream' : 'ext://sys.stdbout',
1834 },
1835 },
1836 'loggers' : {
1837 'compiler.parser' : {
1838 'level' : 'DEBUG',
1839 'handlers' : ['hand1'],
1840 },
1841 },
1842 'root' : {
1843 'level' : 'WARNING',
1844 },
1845 }
1846
1847 #As config1 but with a misspelt level on a handler
1848 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001849 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001850 'formatters': {
1851 'form1' : {
1852 'format' : '%(levelname)s ++ %(message)s',
1853 },
1854 },
1855 'handlers' : {
1856 'hand1' : {
1857 'class' : 'logging.StreamHandler',
1858 'formatter' : 'form1',
1859 'level' : 'NTOSET',
1860 'stream' : 'ext://sys.stdout',
1861 },
1862 },
1863 'loggers' : {
1864 'compiler.parser' : {
1865 'level' : 'DEBUG',
1866 'handlers' : ['hand1'],
1867 },
1868 },
1869 'root' : {
1870 'level' : 'WARNING',
1871 },
1872 }
1873
1874
1875 #As config1 but with a misspelt level on a logger
1876 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001877 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001878 'formatters': {
1879 'form1' : {
1880 'format' : '%(levelname)s ++ %(message)s',
1881 },
1882 },
1883 'handlers' : {
1884 'hand1' : {
1885 'class' : 'logging.StreamHandler',
1886 'formatter' : 'form1',
1887 'level' : 'NOTSET',
1888 'stream' : 'ext://sys.stdout',
1889 },
1890 },
1891 'loggers' : {
1892 'compiler.parser' : {
1893 'level' : 'DEBUG',
1894 'handlers' : ['hand1'],
1895 },
1896 },
1897 'root' : {
1898 'level' : 'WRANING',
1899 },
1900 }
1901
1902 # config3 has a less subtle configuration error
1903 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001904 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001905 'formatters': {
1906 'form1' : {
1907 'format' : '%(levelname)s ++ %(message)s',
1908 },
1909 },
1910 'handlers' : {
1911 'hand1' : {
1912 'class' : 'logging.StreamHandler',
1913 'formatter' : 'misspelled_name',
1914 'level' : 'NOTSET',
1915 'stream' : 'ext://sys.stdout',
1916 },
1917 },
1918 'loggers' : {
1919 'compiler.parser' : {
1920 'level' : 'DEBUG',
1921 'handlers' : ['hand1'],
1922 },
1923 },
1924 'root' : {
1925 'level' : 'WARNING',
1926 },
1927 }
1928
1929 # config4 specifies a custom formatter class to be loaded
1930 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001931 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001932 'formatters': {
1933 'form1' : {
1934 '()' : __name__ + '.ExceptionFormatter',
1935 'format' : '%(levelname)s:%(name)s:%(message)s',
1936 },
1937 },
1938 'handlers' : {
1939 'hand1' : {
1940 'class' : 'logging.StreamHandler',
1941 'formatter' : 'form1',
1942 'level' : 'NOTSET',
1943 'stream' : 'ext://sys.stdout',
1944 },
1945 },
1946 'root' : {
1947 'level' : 'NOTSET',
1948 'handlers' : ['hand1'],
1949 },
1950 }
1951
1952 # As config4 but using an actual callable rather than a string
1953 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001954 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001955 'formatters': {
1956 'form1' : {
1957 '()' : ExceptionFormatter,
1958 'format' : '%(levelname)s:%(name)s:%(message)s',
1959 },
1960 'form2' : {
1961 '()' : __name__ + '.formatFunc',
1962 'format' : '%(levelname)s:%(name)s:%(message)s',
1963 },
1964 'form3' : {
1965 '()' : formatFunc,
1966 'format' : '%(levelname)s:%(name)s:%(message)s',
1967 },
1968 },
1969 'handlers' : {
1970 'hand1' : {
1971 'class' : 'logging.StreamHandler',
1972 'formatter' : 'form1',
1973 'level' : 'NOTSET',
1974 'stream' : 'ext://sys.stdout',
1975 },
1976 'hand2' : {
1977 '()' : handlerFunc,
1978 },
1979 },
1980 'root' : {
1981 'level' : 'NOTSET',
1982 'handlers' : ['hand1'],
1983 },
1984 }
1985
1986 # config5 specifies a custom handler class to be loaded
1987 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001988 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001989 'formatters': {
1990 'form1' : {
1991 'format' : '%(levelname)s ++ %(message)s',
1992 },
1993 },
1994 'handlers' : {
1995 'hand1' : {
1996 'class' : __name__ + '.CustomHandler',
1997 'formatter' : 'form1',
1998 'level' : 'NOTSET',
1999 'stream' : 'ext://sys.stdout',
2000 },
2001 },
2002 'loggers' : {
2003 'compiler.parser' : {
2004 'level' : 'DEBUG',
2005 'handlers' : ['hand1'],
2006 },
2007 },
2008 'root' : {
2009 'level' : 'WARNING',
2010 },
2011 }
2012
2013 # config6 specifies a custom handler class to be loaded
2014 # but has bad arguments
2015 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002016 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002017 'formatters': {
2018 'form1' : {
2019 'format' : '%(levelname)s ++ %(message)s',
2020 },
2021 },
2022 'handlers' : {
2023 'hand1' : {
2024 'class' : __name__ + '.CustomHandler',
2025 'formatter' : 'form1',
2026 'level' : 'NOTSET',
2027 'stream' : 'ext://sys.stdout',
2028 '9' : 'invalid parameter name',
2029 },
2030 },
2031 'loggers' : {
2032 'compiler.parser' : {
2033 'level' : 'DEBUG',
2034 'handlers' : ['hand1'],
2035 },
2036 },
2037 'root' : {
2038 'level' : 'WARNING',
2039 },
2040 }
2041
2042 #config 7 does not define compiler.parser but defines compiler.lexer
2043 #so compiler.parser should be disabled after applying it
2044 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002045 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002046 'formatters': {
2047 'form1' : {
2048 'format' : '%(levelname)s ++ %(message)s',
2049 },
2050 },
2051 'handlers' : {
2052 'hand1' : {
2053 'class' : 'logging.StreamHandler',
2054 'formatter' : 'form1',
2055 'level' : 'NOTSET',
2056 'stream' : 'ext://sys.stdout',
2057 },
2058 },
2059 'loggers' : {
2060 'compiler.lexer' : {
2061 'level' : 'DEBUG',
2062 'handlers' : ['hand1'],
2063 },
2064 },
2065 'root' : {
2066 'level' : 'WARNING',
2067 },
2068 }
2069
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002070 # config8 defines both compiler and compiler.lexer
2071 # so compiler.parser should not be disabled (since
2072 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002073 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002074 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002075 'disable_existing_loggers' : False,
2076 'formatters': {
2077 'form1' : {
2078 'format' : '%(levelname)s ++ %(message)s',
2079 },
2080 },
2081 'handlers' : {
2082 'hand1' : {
2083 'class' : 'logging.StreamHandler',
2084 'formatter' : 'form1',
2085 'level' : 'NOTSET',
2086 'stream' : 'ext://sys.stdout',
2087 },
2088 },
2089 'loggers' : {
2090 'compiler' : {
2091 'level' : 'DEBUG',
2092 'handlers' : ['hand1'],
2093 },
2094 'compiler.lexer' : {
2095 },
2096 },
2097 'root' : {
2098 'level' : 'WARNING',
2099 },
2100 }
2101
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002102 # config8a disables existing loggers
2103 config8a = {
2104 'version': 1,
2105 'disable_existing_loggers' : True,
2106 'formatters': {
2107 'form1' : {
2108 'format' : '%(levelname)s ++ %(message)s',
2109 },
2110 },
2111 'handlers' : {
2112 'hand1' : {
2113 'class' : 'logging.StreamHandler',
2114 'formatter' : 'form1',
2115 'level' : 'NOTSET',
2116 'stream' : 'ext://sys.stdout',
2117 },
2118 },
2119 'loggers' : {
2120 'compiler' : {
2121 'level' : 'DEBUG',
2122 'handlers' : ['hand1'],
2123 },
2124 'compiler.lexer' : {
2125 },
2126 },
2127 'root' : {
2128 'level' : 'WARNING',
2129 },
2130 }
2131
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002132 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002133 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002134 'formatters': {
2135 'form1' : {
2136 'format' : '%(levelname)s ++ %(message)s',
2137 },
2138 },
2139 'handlers' : {
2140 'hand1' : {
2141 'class' : 'logging.StreamHandler',
2142 'formatter' : 'form1',
2143 'level' : 'WARNING',
2144 'stream' : 'ext://sys.stdout',
2145 },
2146 },
2147 'loggers' : {
2148 'compiler.parser' : {
2149 'level' : 'WARNING',
2150 'handlers' : ['hand1'],
2151 },
2152 },
2153 'root' : {
2154 'level' : 'NOTSET',
2155 },
2156 }
2157
2158 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002159 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002160 'incremental' : True,
2161 'handlers' : {
2162 'hand1' : {
2163 'level' : 'WARNING',
2164 },
2165 },
2166 'loggers' : {
2167 'compiler.parser' : {
2168 'level' : 'INFO',
2169 },
2170 },
2171 }
2172
2173 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002174 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002175 'incremental' : True,
2176 'handlers' : {
2177 'hand1' : {
2178 'level' : 'INFO',
2179 },
2180 },
2181 'loggers' : {
2182 'compiler.parser' : {
2183 'level' : 'INFO',
2184 },
2185 },
2186 }
2187
2188 #As config1 but with a filter added
2189 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002190 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002191 'formatters': {
2192 'form1' : {
2193 'format' : '%(levelname)s ++ %(message)s',
2194 },
2195 },
2196 'filters' : {
2197 'filt1' : {
2198 'name' : 'compiler.parser',
2199 },
2200 },
2201 'handlers' : {
2202 'hand1' : {
2203 'class' : 'logging.StreamHandler',
2204 'formatter' : 'form1',
2205 'level' : 'NOTSET',
2206 'stream' : 'ext://sys.stdout',
2207 'filters' : ['filt1'],
2208 },
2209 },
2210 'loggers' : {
2211 'compiler.parser' : {
2212 'level' : 'DEBUG',
2213 'filters' : ['filt1'],
2214 },
2215 },
2216 'root' : {
2217 'level' : 'WARNING',
2218 'handlers' : ['hand1'],
2219 },
2220 }
2221
2222 #As config1 but using cfg:// references
2223 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002224 'version': 1,
2225 'true_formatters': {
2226 'form1' : {
2227 'format' : '%(levelname)s ++ %(message)s',
2228 },
2229 },
2230 'handler_configs': {
2231 'hand1' : {
2232 'class' : 'logging.StreamHandler',
2233 'formatter' : 'form1',
2234 'level' : 'NOTSET',
2235 'stream' : 'ext://sys.stdout',
2236 },
2237 },
2238 'formatters' : 'cfg://true_formatters',
2239 'handlers' : {
2240 'hand1' : 'cfg://handler_configs[hand1]',
2241 },
2242 'loggers' : {
2243 'compiler.parser' : {
2244 'level' : 'DEBUG',
2245 'handlers' : ['hand1'],
2246 },
2247 },
2248 'root' : {
2249 'level' : 'WARNING',
2250 },
2251 }
2252
2253 #As config11 but missing the version key
2254 config12 = {
2255 'true_formatters': {
2256 'form1' : {
2257 'format' : '%(levelname)s ++ %(message)s',
2258 },
2259 },
2260 'handler_configs': {
2261 'hand1' : {
2262 'class' : 'logging.StreamHandler',
2263 'formatter' : 'form1',
2264 'level' : 'NOTSET',
2265 'stream' : 'ext://sys.stdout',
2266 },
2267 },
2268 'formatters' : 'cfg://true_formatters',
2269 'handlers' : {
2270 'hand1' : 'cfg://handler_configs[hand1]',
2271 },
2272 'loggers' : {
2273 'compiler.parser' : {
2274 'level' : 'DEBUG',
2275 'handlers' : ['hand1'],
2276 },
2277 },
2278 'root' : {
2279 'level' : 'WARNING',
2280 },
2281 }
2282
2283 #As config11 but using an unsupported version
2284 config13 = {
2285 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002286 'true_formatters': {
2287 'form1' : {
2288 'format' : '%(levelname)s ++ %(message)s',
2289 },
2290 },
2291 'handler_configs': {
2292 'hand1' : {
2293 'class' : 'logging.StreamHandler',
2294 'formatter' : 'form1',
2295 'level' : 'NOTSET',
2296 'stream' : 'ext://sys.stdout',
2297 },
2298 },
2299 'formatters' : 'cfg://true_formatters',
2300 'handlers' : {
2301 'hand1' : 'cfg://handler_configs[hand1]',
2302 },
2303 'loggers' : {
2304 'compiler.parser' : {
2305 'level' : 'DEBUG',
2306 'handlers' : ['hand1'],
2307 },
2308 },
2309 'root' : {
2310 'level' : 'WARNING',
2311 },
2312 }
2313
2314 def apply_config(self, conf):
2315 logging.config.dictConfig(conf)
2316
2317 def test_config0_ok(self):
2318 # A simple config which overrides the default settings.
2319 with captured_stdout() as output:
2320 self.apply_config(self.config0)
2321 logger = logging.getLogger()
2322 # Won't output anything
2323 logger.info(self.next_message())
2324 # Outputs a message
2325 logger.error(self.next_message())
2326 self.assert_log_lines([
2327 ('ERROR', '2'),
2328 ], stream=output)
2329 # Original logger output is empty.
2330 self.assert_log_lines([])
2331
2332 def test_config1_ok(self, config=config1):
2333 # A config defining a sub-parser as well.
2334 with captured_stdout() as output:
2335 self.apply_config(config)
2336 logger = logging.getLogger("compiler.parser")
2337 # Both will output a message
2338 logger.info(self.next_message())
2339 logger.error(self.next_message())
2340 self.assert_log_lines([
2341 ('INFO', '1'),
2342 ('ERROR', '2'),
2343 ], stream=output)
2344 # Original logger output is empty.
2345 self.assert_log_lines([])
2346
2347 def test_config2_failure(self):
2348 # A simple config which overrides the default settings.
2349 self.assertRaises(Exception, self.apply_config, self.config2)
2350
2351 def test_config2a_failure(self):
2352 # A simple config which overrides the default settings.
2353 self.assertRaises(Exception, self.apply_config, self.config2a)
2354
2355 def test_config2b_failure(self):
2356 # A simple config which overrides the default settings.
2357 self.assertRaises(Exception, self.apply_config, self.config2b)
2358
2359 def test_config3_failure(self):
2360 # A simple config which overrides the default settings.
2361 self.assertRaises(Exception, self.apply_config, self.config3)
2362
2363 def test_config4_ok(self):
2364 # A config specifying a custom formatter class.
2365 with captured_stdout() as output:
2366 self.apply_config(self.config4)
2367 #logger = logging.getLogger()
2368 try:
2369 raise RuntimeError()
2370 except RuntimeError:
2371 logging.exception("just testing")
2372 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002373 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002374 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2375 # Original logger output is empty
2376 self.assert_log_lines([])
2377
2378 def test_config4a_ok(self):
2379 # A config specifying a custom formatter class.
2380 with captured_stdout() as output:
2381 self.apply_config(self.config4a)
2382 #logger = logging.getLogger()
2383 try:
2384 raise RuntimeError()
2385 except RuntimeError:
2386 logging.exception("just testing")
2387 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002388 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002389 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2390 # Original logger output is empty
2391 self.assert_log_lines([])
2392
2393 def test_config5_ok(self):
2394 self.test_config1_ok(config=self.config5)
2395
2396 def test_config6_failure(self):
2397 self.assertRaises(Exception, self.apply_config, self.config6)
2398
2399 def test_config7_ok(self):
2400 with captured_stdout() as output:
2401 self.apply_config(self.config1)
2402 logger = logging.getLogger("compiler.parser")
2403 # Both will output a message
2404 logger.info(self.next_message())
2405 logger.error(self.next_message())
2406 self.assert_log_lines([
2407 ('INFO', '1'),
2408 ('ERROR', '2'),
2409 ], stream=output)
2410 # Original logger output is empty.
2411 self.assert_log_lines([])
2412 with captured_stdout() as output:
2413 self.apply_config(self.config7)
2414 logger = logging.getLogger("compiler.parser")
2415 self.assertTrue(logger.disabled)
2416 logger = logging.getLogger("compiler.lexer")
2417 # Both will output a message
2418 logger.info(self.next_message())
2419 logger.error(self.next_message())
2420 self.assert_log_lines([
2421 ('INFO', '3'),
2422 ('ERROR', '4'),
2423 ], stream=output)
2424 # Original logger output is empty.
2425 self.assert_log_lines([])
2426
2427 #Same as test_config_7_ok but don't disable old loggers.
2428 def test_config_8_ok(self):
2429 with captured_stdout() as output:
2430 self.apply_config(self.config1)
2431 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002432 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002433 logger.info(self.next_message())
2434 logger.error(self.next_message())
2435 self.assert_log_lines([
2436 ('INFO', '1'),
2437 ('ERROR', '2'),
2438 ], stream=output)
2439 # Original logger output is empty.
2440 self.assert_log_lines([])
2441 with captured_stdout() as output:
2442 self.apply_config(self.config8)
2443 logger = logging.getLogger("compiler.parser")
2444 self.assertFalse(logger.disabled)
2445 # Both will output a message
2446 logger.info(self.next_message())
2447 logger.error(self.next_message())
2448 logger = logging.getLogger("compiler.lexer")
2449 # Both will output a message
2450 logger.info(self.next_message())
2451 logger.error(self.next_message())
2452 self.assert_log_lines([
2453 ('INFO', '3'),
2454 ('ERROR', '4'),
2455 ('INFO', '5'),
2456 ('ERROR', '6'),
2457 ], stream=output)
2458 # Original logger output is empty.
2459 self.assert_log_lines([])
2460
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002461 def test_config_8a_ok(self):
2462 with captured_stdout() as output:
2463 self.apply_config(self.config1a)
2464 logger = logging.getLogger("compiler.parser")
2465 # See issue #11424. compiler-hyphenated sorts
2466 # between compiler and compiler.xyz and this
2467 # was preventing compiler.xyz from being included
2468 # in the child loggers of compiler because of an
2469 # overzealous loop termination condition.
2470 hyphenated = logging.getLogger('compiler-hyphenated')
2471 # All will output a message
2472 logger.info(self.next_message())
2473 logger.error(self.next_message())
2474 hyphenated.critical(self.next_message())
2475 self.assert_log_lines([
2476 ('INFO', '1'),
2477 ('ERROR', '2'),
2478 ('CRITICAL', '3'),
2479 ], stream=output)
2480 # Original logger output is empty.
2481 self.assert_log_lines([])
2482 with captured_stdout() as output:
2483 self.apply_config(self.config8a)
2484 logger = logging.getLogger("compiler.parser")
2485 self.assertFalse(logger.disabled)
2486 # Both will output a message
2487 logger.info(self.next_message())
2488 logger.error(self.next_message())
2489 logger = logging.getLogger("compiler.lexer")
2490 # Both will output a message
2491 logger.info(self.next_message())
2492 logger.error(self.next_message())
2493 # Will not appear
2494 hyphenated.critical(self.next_message())
2495 self.assert_log_lines([
2496 ('INFO', '4'),
2497 ('ERROR', '5'),
2498 ('INFO', '6'),
2499 ('ERROR', '7'),
2500 ], stream=output)
2501 # Original logger output is empty.
2502 self.assert_log_lines([])
2503
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002504 def test_config_9_ok(self):
2505 with captured_stdout() as output:
2506 self.apply_config(self.config9)
2507 logger = logging.getLogger("compiler.parser")
2508 #Nothing will be output since both handler and logger are set to WARNING
2509 logger.info(self.next_message())
2510 self.assert_log_lines([], stream=output)
2511 self.apply_config(self.config9a)
2512 #Nothing will be output since both handler is still set to WARNING
2513 logger.info(self.next_message())
2514 self.assert_log_lines([], stream=output)
2515 self.apply_config(self.config9b)
2516 #Message should now be output
2517 logger.info(self.next_message())
2518 self.assert_log_lines([
2519 ('INFO', '3'),
2520 ], stream=output)
2521
2522 def test_config_10_ok(self):
2523 with captured_stdout() as output:
2524 self.apply_config(self.config10)
2525 logger = logging.getLogger("compiler.parser")
2526 logger.warning(self.next_message())
2527 logger = logging.getLogger('compiler')
2528 #Not output, because filtered
2529 logger.warning(self.next_message())
2530 logger = logging.getLogger('compiler.lexer')
2531 #Not output, because filtered
2532 logger.warning(self.next_message())
2533 logger = logging.getLogger("compiler.parser.codegen")
2534 #Output, as not filtered
2535 logger.error(self.next_message())
2536 self.assert_log_lines([
2537 ('WARNING', '1'),
2538 ('ERROR', '4'),
2539 ], stream=output)
2540
2541 def test_config11_ok(self):
2542 self.test_config1_ok(self.config11)
2543
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002544 def test_config12_failure(self):
2545 self.assertRaises(Exception, self.apply_config, self.config12)
2546
2547 def test_config13_failure(self):
2548 self.assertRaises(Exception, self.apply_config, self.config13)
2549
Victor Stinner45df8202010-04-28 22:31:17 +00002550 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002551 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002552 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002553 # Ask for a randomly assigned port (by using port 0)
2554 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002555 t.start()
2556 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002557 # Now get the port allocated
2558 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002559 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002560 try:
2561 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2562 sock.settimeout(2.0)
2563 sock.connect(('localhost', port))
2564
2565 slen = struct.pack('>L', len(text))
2566 s = slen + text
2567 sentsofar = 0
2568 left = len(s)
2569 while left > 0:
2570 sent = sock.send(s[sentsofar:])
2571 sentsofar += sent
2572 left -= sent
2573 sock.close()
2574 finally:
2575 t.ready.wait(2.0)
2576 logging.config.stopListening()
2577 t.join(2.0)
2578
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002579 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002580 def test_listen_config_10_ok(self):
2581 with captured_stdout() as output:
2582 self.setup_via_listener(json.dumps(self.config10))
2583 logger = logging.getLogger("compiler.parser")
2584 logger.warning(self.next_message())
2585 logger = logging.getLogger('compiler')
2586 #Not output, because filtered
2587 logger.warning(self.next_message())
2588 logger = logging.getLogger('compiler.lexer')
2589 #Not output, because filtered
2590 logger.warning(self.next_message())
2591 logger = logging.getLogger("compiler.parser.codegen")
2592 #Output, as not filtered
2593 logger.error(self.next_message())
2594 self.assert_log_lines([
2595 ('WARNING', '1'),
2596 ('ERROR', '4'),
2597 ], stream=output)
2598
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002599 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002600 def test_listen_config_1_ok(self):
2601 with captured_stdout() as output:
2602 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2603 logger = logging.getLogger("compiler.parser")
2604 # Both will output a message
2605 logger.info(self.next_message())
2606 logger.error(self.next_message())
2607 self.assert_log_lines([
2608 ('INFO', '1'),
2609 ('ERROR', '2'),
2610 ], stream=output)
2611 # Original logger output is empty.
2612 self.assert_log_lines([])
2613
Vinay Sajip373baef2011-04-26 20:05:24 +01002614 def test_baseconfig(self):
2615 d = {
2616 'atuple': (1, 2, 3),
2617 'alist': ['a', 'b', 'c'],
2618 'adict': {'d': 'e', 'f': 3 },
2619 'nest1': ('g', ('h', 'i'), 'j'),
2620 'nest2': ['k', ['l', 'm'], 'n'],
2621 'nest3': ['o', 'cfg://alist', 'p'],
2622 }
2623 bc = logging.config.BaseConfigurator(d)
2624 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2625 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2626 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2627 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2628 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2629 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2630 v = bc.convert('cfg://nest3')
2631 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2632 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2633 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2634 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002635
2636class ManagerTest(BaseTest):
2637 def test_manager_loggerclass(self):
2638 logged = []
2639
2640 class MyLogger(logging.Logger):
2641 def _log(self, level, msg, args, exc_info=None, extra=None):
2642 logged.append(msg)
2643
2644 man = logging.Manager(None)
2645 self.assertRaises(TypeError, man.setLoggerClass, int)
2646 man.setLoggerClass(MyLogger)
2647 logger = man.getLogger('test')
2648 logger.warning('should appear in logged')
2649 logging.warning('should not appear in logged')
2650
2651 self.assertEqual(logged, ['should appear in logged'])
2652
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002653 def test_set_log_record_factory(self):
2654 man = logging.Manager(None)
2655 expected = object()
2656 man.setLogRecordFactory(expected)
2657 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002658
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002659class ChildLoggerTest(BaseTest):
2660 def test_child_loggers(self):
2661 r = logging.getLogger()
2662 l1 = logging.getLogger('abc')
2663 l2 = logging.getLogger('def.ghi')
2664 c1 = r.getChild('xyz')
2665 c2 = r.getChild('uvw.xyz')
2666 self.assertTrue(c1 is logging.getLogger('xyz'))
2667 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2668 c1 = l1.getChild('def')
2669 c2 = c1.getChild('ghi')
2670 c3 = l1.getChild('def.ghi')
2671 self.assertTrue(c1 is logging.getLogger('abc.def'))
2672 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2673 self.assertTrue(c2 is c3)
2674
2675
Vinay Sajip6fac8172010-10-19 20:44:14 +00002676class DerivedLogRecord(logging.LogRecord):
2677 pass
2678
Vinay Sajip61561522010-12-03 11:50:38 +00002679class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002680
2681 def setUp(self):
2682 class CheckingFilter(logging.Filter):
2683 def __init__(self, cls):
2684 self.cls = cls
2685
2686 def filter(self, record):
2687 t = type(record)
2688 if t is not self.cls:
2689 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2690 self.cls)
2691 raise TypeError(msg)
2692 return True
2693
2694 BaseTest.setUp(self)
2695 self.filter = CheckingFilter(DerivedLogRecord)
2696 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002697 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002698
2699 def tearDown(self):
2700 self.root_logger.removeFilter(self.filter)
2701 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002702 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002703
2704 def test_logrecord_class(self):
2705 self.assertRaises(TypeError, self.root_logger.warning,
2706 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002707 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002708 self.root_logger.error(self.next_message())
2709 self.assert_log_lines([
2710 ('root', 'ERROR', '2'),
2711 ])
2712
2713
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002714class QueueHandlerTest(BaseTest):
2715 # Do not bother with a logger name group.
2716 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2717
2718 def setUp(self):
2719 BaseTest.setUp(self)
2720 self.queue = queue.Queue(-1)
2721 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2722 self.que_logger = logging.getLogger('que')
2723 self.que_logger.propagate = False
2724 self.que_logger.setLevel(logging.WARNING)
2725 self.que_logger.addHandler(self.que_hdlr)
2726
2727 def tearDown(self):
2728 self.que_hdlr.close()
2729 BaseTest.tearDown(self)
2730
2731 def test_queue_handler(self):
2732 self.que_logger.debug(self.next_message())
2733 self.assertRaises(queue.Empty, self.queue.get_nowait)
2734 self.que_logger.info(self.next_message())
2735 self.assertRaises(queue.Empty, self.queue.get_nowait)
2736 msg = self.next_message()
2737 self.que_logger.warning(msg)
2738 data = self.queue.get_nowait()
2739 self.assertTrue(isinstance(data, logging.LogRecord))
2740 self.assertEqual(data.name, self.que_logger.name)
2741 self.assertEqual((data.msg, data.args), (msg, None))
2742
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002743 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2744 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002745 def test_queue_listener(self):
2746 handler = TestHandler(Matcher())
2747 listener = logging.handlers.QueueListener(self.queue, handler)
2748 listener.start()
2749 try:
2750 self.que_logger.warning(self.next_message())
2751 self.que_logger.error(self.next_message())
2752 self.que_logger.critical(self.next_message())
2753 finally:
2754 listener.stop()
2755 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2756 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2757 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2758
Vinay Sajip37eb3382011-04-26 20:26:41 +01002759ZERO = datetime.timedelta(0)
2760
2761class UTC(datetime.tzinfo):
2762 def utcoffset(self, dt):
2763 return ZERO
2764
2765 dst = utcoffset
2766
2767 def tzname(self, dt):
2768 return 'UTC'
2769
2770utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002771
Vinay Sajipa39c5712010-10-25 13:57:39 +00002772class FormatterTest(unittest.TestCase):
2773 def setUp(self):
2774 self.common = {
2775 'name': 'formatter.test',
2776 'level': logging.DEBUG,
2777 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2778 'lineno': 42,
2779 'exc_info': None,
2780 'func': None,
2781 'msg': 'Message with %d %s',
2782 'args': (2, 'placeholders'),
2783 }
2784 self.variants = {
2785 }
2786
2787 def get_record(self, name=None):
2788 result = dict(self.common)
2789 if name is not None:
2790 result.update(self.variants[name])
2791 return logging.makeLogRecord(result)
2792
2793 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002794 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002795 r = self.get_record()
2796 f = logging.Formatter('${%(message)s}')
2797 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2798 f = logging.Formatter('%(random)s')
2799 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002800 self.assertFalse(f.usesTime())
2801 f = logging.Formatter('%(asctime)s')
2802 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002803 f = logging.Formatter('%(asctime)-15s')
2804 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002805 f = logging.Formatter('asctime')
2806 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002807
2808 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002809 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002810 r = self.get_record()
2811 f = logging.Formatter('$%{message}%$', style='{')
2812 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2813 f = logging.Formatter('{random}', style='{')
2814 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002815 self.assertFalse(f.usesTime())
2816 f = logging.Formatter('{asctime}', style='{')
2817 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002818 f = logging.Formatter('{asctime!s:15}', style='{')
2819 self.assertTrue(f.usesTime())
2820 f = logging.Formatter('{asctime:15}', style='{')
2821 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002822 f = logging.Formatter('asctime', style='{')
2823 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002824
2825 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002826 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002827 r = self.get_record()
2828 f = logging.Formatter('$message', style='$')
2829 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2830 f = logging.Formatter('$$%${message}%$$', style='$')
2831 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2832 f = logging.Formatter('${random}', style='$')
2833 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002834 self.assertFalse(f.usesTime())
2835 f = logging.Formatter('${asctime}', style='$')
2836 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002837 f = logging.Formatter('${asctime', style='$')
2838 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002839 f = logging.Formatter('$asctime', style='$')
2840 self.assertTrue(f.usesTime())
2841 f = logging.Formatter('asctime', style='$')
2842 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002843
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002844 def test_invalid_style(self):
2845 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2846
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002847 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002848 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002849 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002850 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002851 r.msecs = 123
2852 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002853 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002854 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2855 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002856 f.format(r)
2857 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2858
2859class TestBufferingFormatter(logging.BufferingFormatter):
2860 def formatHeader(self, records):
2861 return '[(%d)' % len(records)
2862
2863 def formatFooter(self, records):
2864 return '(%d)]' % len(records)
2865
2866class BufferingFormatterTest(unittest.TestCase):
2867 def setUp(self):
2868 self.records = [
2869 logging.makeLogRecord({'msg': 'one'}),
2870 logging.makeLogRecord({'msg': 'two'}),
2871 ]
2872
2873 def test_default(self):
2874 f = logging.BufferingFormatter()
2875 self.assertEqual('', f.format([]))
2876 self.assertEqual('onetwo', f.format(self.records))
2877
2878 def test_custom(self):
2879 f = TestBufferingFormatter()
2880 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2881 lf = logging.Formatter('<%(message)s>')
2882 f = TestBufferingFormatter(lf)
2883 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002884
2885class ExceptionTest(BaseTest):
2886 def test_formatting(self):
2887 r = self.root_logger
2888 h = RecordingHandler()
2889 r.addHandler(h)
2890 try:
2891 raise RuntimeError('deliberate mistake')
2892 except:
2893 logging.exception('failed', stack_info=True)
2894 r.removeHandler(h)
2895 h.close()
2896 r = h.records[0]
2897 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2898 'call last):\n'))
2899 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2900 'deliberate mistake'))
2901 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2902 'call last):\n'))
2903 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2904 'stack_info=True)'))
2905
2906
Vinay Sajip5a27d402010-12-10 11:42:57 +00002907class LastResortTest(BaseTest):
2908 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002909 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002910 root = self.root_logger
2911 root.removeHandler(self.root_hdlr)
2912 old_stderr = sys.stderr
2913 old_lastresort = logging.lastResort
2914 old_raise_exceptions = logging.raiseExceptions
2915 try:
2916 sys.stderr = sio = io.StringIO()
2917 root.warning('This is your final chance!')
2918 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2919 #No handlers and no last resort, so 'No handlers' message
2920 logging.lastResort = None
2921 sys.stderr = sio = io.StringIO()
2922 root.warning('This is your final chance!')
2923 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2924 # 'No handlers' message only printed once
2925 sys.stderr = sio = io.StringIO()
2926 root.warning('This is your final chance!')
2927 self.assertEqual(sio.getvalue(), '')
2928 root.manager.emittedNoHandlerWarning = False
2929 #If raiseExceptions is False, no message is printed
2930 logging.raiseExceptions = False
2931 sys.stderr = sio = io.StringIO()
2932 root.warning('This is your final chance!')
2933 self.assertEqual(sio.getvalue(), '')
2934 finally:
2935 sys.stderr = old_stderr
2936 root.addHandler(self.root_hdlr)
2937 logging.lastResort = old_lastresort
2938 logging.raiseExceptions = old_raise_exceptions
2939
2940
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002941class FakeHandler:
2942
2943 def __init__(self, identifier, called):
2944 for method in ('acquire', 'flush', 'close', 'release'):
2945 setattr(self, method, self.record_call(identifier, method, called))
2946
2947 def record_call(self, identifier, method_name, called):
2948 def inner():
2949 called.append('{} - {}'.format(identifier, method_name))
2950 return inner
2951
2952
2953class RecordingHandler(logging.NullHandler):
2954
2955 def __init__(self, *args, **kwargs):
2956 super(RecordingHandler, self).__init__(*args, **kwargs)
2957 self.records = []
2958
2959 def handle(self, record):
2960 """Keep track of all the emitted records."""
2961 self.records.append(record)
2962
2963
2964class ShutdownTest(BaseTest):
2965
Vinay Sajip5e66b162011-04-20 15:41:14 +01002966 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002967
2968 def setUp(self):
2969 super(ShutdownTest, self).setUp()
2970 self.called = []
2971
2972 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002973 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002974
2975 def raise_error(self, error):
2976 def inner():
2977 raise error()
2978 return inner
2979
2980 def test_no_failure(self):
2981 # create some fake handlers
2982 handler0 = FakeHandler(0, self.called)
2983 handler1 = FakeHandler(1, self.called)
2984 handler2 = FakeHandler(2, self.called)
2985
2986 # create live weakref to those handlers
2987 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2988
2989 logging.shutdown(handlerList=list(handlers))
2990
2991 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2992 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2993 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2994 self.assertEqual(expected, self.called)
2995
2996 def _test_with_failure_in_method(self, method, error):
2997 handler = FakeHandler(0, self.called)
2998 setattr(handler, method, self.raise_error(error))
2999 handlers = [logging.weakref.ref(handler)]
3000
3001 logging.shutdown(handlerList=list(handlers))
3002
3003 self.assertEqual('0 - release', self.called[-1])
3004
3005 def test_with_ioerror_in_acquire(self):
3006 self._test_with_failure_in_method('acquire', IOError)
3007
3008 def test_with_ioerror_in_flush(self):
3009 self._test_with_failure_in_method('flush', IOError)
3010
3011 def test_with_ioerror_in_close(self):
3012 self._test_with_failure_in_method('close', IOError)
3013
3014 def test_with_valueerror_in_acquire(self):
3015 self._test_with_failure_in_method('acquire', ValueError)
3016
3017 def test_with_valueerror_in_flush(self):
3018 self._test_with_failure_in_method('flush', ValueError)
3019
3020 def test_with_valueerror_in_close(self):
3021 self._test_with_failure_in_method('close', ValueError)
3022
3023 def test_with_other_error_in_acquire_without_raise(self):
3024 logging.raiseExceptions = False
3025 self._test_with_failure_in_method('acquire', IndexError)
3026
3027 def test_with_other_error_in_flush_without_raise(self):
3028 logging.raiseExceptions = False
3029 self._test_with_failure_in_method('flush', IndexError)
3030
3031 def test_with_other_error_in_close_without_raise(self):
3032 logging.raiseExceptions = False
3033 self._test_with_failure_in_method('close', IndexError)
3034
3035 def test_with_other_error_in_acquire_with_raise(self):
3036 logging.raiseExceptions = True
3037 self.assertRaises(IndexError, self._test_with_failure_in_method,
3038 'acquire', IndexError)
3039
3040 def test_with_other_error_in_flush_with_raise(self):
3041 logging.raiseExceptions = True
3042 self.assertRaises(IndexError, self._test_with_failure_in_method,
3043 'flush', IndexError)
3044
3045 def test_with_other_error_in_close_with_raise(self):
3046 logging.raiseExceptions = True
3047 self.assertRaises(IndexError, self._test_with_failure_in_method,
3048 'close', IndexError)
3049
3050
3051class ModuleLevelMiscTest(BaseTest):
3052
Vinay Sajip5e66b162011-04-20 15:41:14 +01003053 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003054
3055 def test_disable(self):
3056 old_disable = logging.root.manager.disable
3057 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003058 self.assertEqual(old_disable, 0)
3059 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003060
3061 logging.disable(83)
3062 self.assertEqual(logging.root.manager.disable, 83)
3063
3064 def _test_log(self, method, level=None):
3065 called = []
3066 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003067 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003068
3069 recording = RecordingHandler()
3070 logging.root.addHandler(recording)
3071
3072 log_method = getattr(logging, method)
3073 if level is not None:
3074 log_method(level, "test me: %r", recording)
3075 else:
3076 log_method("test me: %r", recording)
3077
3078 self.assertEqual(len(recording.records), 1)
3079 record = recording.records[0]
3080 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3081
3082 expected_level = level if level is not None else getattr(logging, method.upper())
3083 self.assertEqual(record.levelno, expected_level)
3084
3085 # basicConfig was not called!
3086 self.assertEqual(called, [])
3087
3088 def test_log(self):
3089 self._test_log('log', logging.ERROR)
3090
3091 def test_debug(self):
3092 self._test_log('debug')
3093
3094 def test_info(self):
3095 self._test_log('info')
3096
3097 def test_warning(self):
3098 self._test_log('warning')
3099
3100 def test_error(self):
3101 self._test_log('error')
3102
3103 def test_critical(self):
3104 self._test_log('critical')
3105
3106 def test_set_logger_class(self):
3107 self.assertRaises(TypeError, logging.setLoggerClass, object)
3108
3109 class MyLogger(logging.Logger):
3110 pass
3111
3112 logging.setLoggerClass(MyLogger)
3113 self.assertEqual(logging.getLoggerClass(), MyLogger)
3114
3115 logging.setLoggerClass(logging.Logger)
3116 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3117
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003118class LogRecordTest(BaseTest):
3119 def test_str_rep(self):
3120 r = logging.makeLogRecord({})
3121 s = str(r)
3122 self.assertTrue(s.startswith('<LogRecord: '))
3123 self.assertTrue(s.endswith('>'))
3124
3125 def test_dict_arg(self):
3126 h = RecordingHandler()
3127 r = logging.getLogger()
3128 r.addHandler(h)
3129 d = {'less' : 'more' }
3130 logging.warning('less is %(less)s', d)
3131 self.assertIs(h.records[0].args, d)
3132 self.assertEqual(h.records[0].message, 'less is more')
3133 r.removeHandler(h)
3134 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003135
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003136 def test_multiprocessing(self):
3137 r = logging.makeLogRecord({})
3138 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003139 try:
3140 import multiprocessing as mp
3141 r = logging.makeLogRecord({})
3142 self.assertEqual(r.processName, mp.current_process().name)
3143 except ImportError:
3144 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003145
3146 def test_optional(self):
3147 r = logging.makeLogRecord({})
3148 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003149 if threading:
3150 NOT_NONE(r.thread)
3151 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003152 NOT_NONE(r.process)
3153 NOT_NONE(r.processName)
3154 log_threads = logging.logThreads
3155 log_processes = logging.logProcesses
3156 log_multiprocessing = logging.logMultiprocessing
3157 try:
3158 logging.logThreads = False
3159 logging.logProcesses = False
3160 logging.logMultiprocessing = False
3161 r = logging.makeLogRecord({})
3162 NONE = self.assertIsNone
3163 NONE(r.thread)
3164 NONE(r.threadName)
3165 NONE(r.process)
3166 NONE(r.processName)
3167 finally:
3168 logging.logThreads = log_threads
3169 logging.logProcesses = log_processes
3170 logging.logMultiprocessing = log_multiprocessing
3171
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003172class BasicConfigTest(unittest.TestCase):
3173
Vinay Sajip95bf5042011-04-20 11:50:56 +01003174 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003175
3176 def setUp(self):
3177 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003178 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003179 self.saved_handlers = logging._handlers.copy()
3180 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003181 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003182 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003183 logging.root.handlers = []
3184
3185 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003186 for h in logging.root.handlers[:]:
3187 logging.root.removeHandler(h)
3188 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003189 super(BasicConfigTest, self).tearDown()
3190
Vinay Sajip3def7e02011-04-20 10:58:06 +01003191 def cleanup(self):
3192 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003193 logging._handlers.clear()
3194 logging._handlers.update(self.saved_handlers)
3195 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003196 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003197
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003198 def test_no_kwargs(self):
3199 logging.basicConfig()
3200
3201 # handler defaults to a StreamHandler to sys.stderr
3202 self.assertEqual(len(logging.root.handlers), 1)
3203 handler = logging.root.handlers[0]
3204 self.assertIsInstance(handler, logging.StreamHandler)
3205 self.assertEqual(handler.stream, sys.stderr)
3206
3207 formatter = handler.formatter
3208 # format defaults to logging.BASIC_FORMAT
3209 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3210 # datefmt defaults to None
3211 self.assertIsNone(formatter.datefmt)
3212 # style defaults to %
3213 self.assertIsInstance(formatter._style, logging.PercentStyle)
3214
3215 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003216 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003217
3218 def test_filename(self):
3219 logging.basicConfig(filename='test.log')
3220
3221 self.assertEqual(len(logging.root.handlers), 1)
3222 handler = logging.root.handlers[0]
3223 self.assertIsInstance(handler, logging.FileHandler)
3224
3225 expected = logging.FileHandler('test.log', 'a')
3226 self.addCleanup(expected.close)
3227 self.assertEqual(handler.stream.mode, expected.stream.mode)
3228 self.assertEqual(handler.stream.name, expected.stream.name)
3229
3230 def test_filemode(self):
3231 logging.basicConfig(filename='test.log', filemode='wb')
3232
3233 handler = logging.root.handlers[0]
3234 expected = logging.FileHandler('test.log', 'wb')
3235 self.addCleanup(expected.close)
3236 self.assertEqual(handler.stream.mode, expected.stream.mode)
3237
3238 def test_stream(self):
3239 stream = io.StringIO()
3240 self.addCleanup(stream.close)
3241 logging.basicConfig(stream=stream)
3242
3243 self.assertEqual(len(logging.root.handlers), 1)
3244 handler = logging.root.handlers[0]
3245 self.assertIsInstance(handler, logging.StreamHandler)
3246 self.assertEqual(handler.stream, stream)
3247
3248 def test_format(self):
3249 logging.basicConfig(format='foo')
3250
3251 formatter = logging.root.handlers[0].formatter
3252 self.assertEqual(formatter._style._fmt, 'foo')
3253
3254 def test_datefmt(self):
3255 logging.basicConfig(datefmt='bar')
3256
3257 formatter = logging.root.handlers[0].formatter
3258 self.assertEqual(formatter.datefmt, 'bar')
3259
3260 def test_style(self):
3261 logging.basicConfig(style='$')
3262
3263 formatter = logging.root.handlers[0].formatter
3264 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3265
3266 def test_level(self):
3267 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003268 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003269
3270 logging.basicConfig(level=57)
3271 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003272 # Test that second call has no effect
3273 logging.basicConfig(level=58)
3274 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003275
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003276 def test_incompatible(self):
3277 assertRaises = self.assertRaises
3278 handlers = [logging.StreamHandler()]
3279 stream = sys.stderr
3280 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3281 stream=stream)
3282 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3283 handlers=handlers)
3284 assertRaises(ValueError, logging.basicConfig, stream=stream,
3285 handlers=handlers)
3286
3287 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003288 handlers = [
3289 logging.StreamHandler(),
3290 logging.StreamHandler(sys.stdout),
3291 logging.StreamHandler(),
3292 ]
3293 f = logging.Formatter()
3294 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003295 logging.basicConfig(handlers=handlers)
3296 self.assertIs(handlers[0], logging.root.handlers[0])
3297 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003298 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003299 self.assertIsNotNone(handlers[0].formatter)
3300 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003301 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003302 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3303
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003304 def _test_log(self, method, level=None):
3305 # logging.root has no handlers so basicConfig should be called
3306 called = []
3307
3308 old_basic_config = logging.basicConfig
3309 def my_basic_config(*a, **kw):
3310 old_basic_config()
3311 old_level = logging.root.level
3312 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003313 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003314 called.append((a, kw))
3315
3316 patch(self, logging, 'basicConfig', my_basic_config)
3317
3318 log_method = getattr(logging, method)
3319 if level is not None:
3320 log_method(level, "test me")
3321 else:
3322 log_method("test me")
3323
3324 # basicConfig was called with no arguments
3325 self.assertEqual(called, [((), {})])
3326
3327 def test_log(self):
3328 self._test_log('log', logging.WARNING)
3329
3330 def test_debug(self):
3331 self._test_log('debug')
3332
3333 def test_info(self):
3334 self._test_log('info')
3335
3336 def test_warning(self):
3337 self._test_log('warning')
3338
3339 def test_error(self):
3340 self._test_log('error')
3341
3342 def test_critical(self):
3343 self._test_log('critical')
3344
3345
3346class LoggerAdapterTest(unittest.TestCase):
3347
3348 def setUp(self):
3349 super(LoggerAdapterTest, self).setUp()
3350 old_handler_list = logging._handlerList[:]
3351
3352 self.recording = RecordingHandler()
3353 self.logger = logging.root
3354 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003355 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003356 self.addCleanup(self.recording.close)
3357
3358 def cleanup():
3359 logging._handlerList[:] = old_handler_list
3360
3361 self.addCleanup(cleanup)
3362 self.addCleanup(logging.shutdown)
3363 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3364
3365 def test_exception(self):
3366 msg = 'testing exception: %r'
3367 exc = None
3368 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003369 1 / 0
3370 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003371 exc = e
3372 self.adapter.exception(msg, self.recording)
3373
3374 self.assertEqual(len(self.recording.records), 1)
3375 record = self.recording.records[0]
3376 self.assertEqual(record.levelno, logging.ERROR)
3377 self.assertEqual(record.msg, msg)
3378 self.assertEqual(record.args, (self.recording,))
3379 self.assertEqual(record.exc_info,
3380 (exc.__class__, exc, exc.__traceback__))
3381
3382 def test_critical(self):
3383 msg = 'critical test! %r'
3384 self.adapter.critical(msg, self.recording)
3385
3386 self.assertEqual(len(self.recording.records), 1)
3387 record = self.recording.records[0]
3388 self.assertEqual(record.levelno, logging.CRITICAL)
3389 self.assertEqual(record.msg, msg)
3390 self.assertEqual(record.args, (self.recording,))
3391
3392 def test_is_enabled_for(self):
3393 old_disable = self.adapter.logger.manager.disable
3394 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003395 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3396 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003397 self.assertFalse(self.adapter.isEnabledFor(32))
3398
3399 def test_has_handlers(self):
3400 self.assertTrue(self.adapter.hasHandlers())
3401
3402 for handler in self.logger.handlers:
3403 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003404
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003405 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003406 self.assertFalse(self.adapter.hasHandlers())
3407
3408
3409class LoggerTest(BaseTest):
3410
3411 def setUp(self):
3412 super(LoggerTest, self).setUp()
3413 self.recording = RecordingHandler()
3414 self.logger = logging.Logger(name='blah')
3415 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003416 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003417 self.addCleanup(self.recording.close)
3418 self.addCleanup(logging.shutdown)
3419
3420 def test_set_invalid_level(self):
3421 self.assertRaises(TypeError, self.logger.setLevel, object())
3422
3423 def test_exception(self):
3424 msg = 'testing exception: %r'
3425 exc = None
3426 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003427 1 / 0
3428 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003429 exc = e
3430 self.logger.exception(msg, self.recording)
3431
3432 self.assertEqual(len(self.recording.records), 1)
3433 record = self.recording.records[0]
3434 self.assertEqual(record.levelno, logging.ERROR)
3435 self.assertEqual(record.msg, msg)
3436 self.assertEqual(record.args, (self.recording,))
3437 self.assertEqual(record.exc_info,
3438 (exc.__class__, exc, exc.__traceback__))
3439
3440 def test_log_invalid_level_with_raise(self):
3441 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003442 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003443
3444 logging.raiseExceptions = True
3445 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3446
3447 def test_log_invalid_level_no_raise(self):
3448 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003449 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003450
3451 logging.raiseExceptions = False
3452 self.logger.log('10', 'test message') # no exception happens
3453
3454 def test_find_caller_with_stack_info(self):
3455 called = []
3456 patch(self, logging.traceback, 'print_stack',
3457 lambda f, file: called.append(file.getvalue()))
3458
3459 self.logger.findCaller(stack_info=True)
3460
3461 self.assertEqual(len(called), 1)
3462 self.assertEqual('Stack (most recent call last):\n', called[0])
3463
3464 def test_make_record_with_extra_overwrite(self):
3465 name = 'my record'
3466 level = 13
3467 fn = lno = msg = args = exc_info = func = sinfo = None
3468 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3469 exc_info, func, sinfo)
3470
3471 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3472 extra = {key: 'some value'}
3473 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3474 fn, lno, msg, args, exc_info,
3475 extra=extra, sinfo=sinfo)
3476
3477 def test_make_record_with_extra_no_overwrite(self):
3478 name = 'my record'
3479 level = 13
3480 fn = lno = msg = args = exc_info = func = sinfo = None
3481 extra = {'valid_key': 'some value'}
3482 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3483 exc_info, extra=extra, sinfo=sinfo)
3484 self.assertIn('valid_key', result.__dict__)
3485
3486 def test_has_handlers(self):
3487 self.assertTrue(self.logger.hasHandlers())
3488
3489 for handler in self.logger.handlers:
3490 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003491 self.assertFalse(self.logger.hasHandlers())
3492
3493 def test_has_handlers_no_propagate(self):
3494 child_logger = logging.getLogger('blah.child')
3495 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003496 self.assertFalse(child_logger.hasHandlers())
3497
3498 def test_is_enabled_for(self):
3499 old_disable = self.logger.manager.disable
3500 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003501 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003502 self.assertFalse(self.logger.isEnabledFor(22))
3503
3504
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003505class BaseFileTest(BaseTest):
3506 "Base class for handler tests that write log files"
3507
3508 def setUp(self):
3509 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003510 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3511 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003512 self.rmfiles = []
3513
3514 def tearDown(self):
3515 for fn in self.rmfiles:
3516 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003517 if os.path.exists(self.fn):
3518 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003519 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003520
3521 def assertLogFile(self, filename):
3522 "Assert a log file is there and register it for deletion"
3523 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003524 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003525 self.rmfiles.append(filename)
3526
3527
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003528class FileHandlerTest(BaseFileTest):
3529 def test_delay(self):
3530 os.unlink(self.fn)
3531 fh = logging.FileHandler(self.fn, delay=True)
3532 self.assertIsNone(fh.stream)
3533 self.assertFalse(os.path.exists(self.fn))
3534 fh.handle(logging.makeLogRecord({}))
3535 self.assertIsNotNone(fh.stream)
3536 self.assertTrue(os.path.exists(self.fn))
3537 fh.close()
3538
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003539class RotatingFileHandlerTest(BaseFileTest):
3540 def next_rec(self):
3541 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3542 self.next_message(), None, None, None)
3543
3544 def test_should_not_rollover(self):
3545 # If maxbytes is zero rollover never occurs
3546 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3547 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003548 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003549
3550 def test_should_rollover(self):
3551 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3552 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003553 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003554
3555 def test_file_created(self):
3556 # checks that the file is created and assumes it was created
3557 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003558 rh = logging.handlers.RotatingFileHandler(self.fn)
3559 rh.emit(self.next_rec())
3560 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003561 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003562
3563 def test_rollover_filenames(self):
3564 rh = logging.handlers.RotatingFileHandler(
3565 self.fn, backupCount=2, maxBytes=1)
3566 rh.emit(self.next_rec())
3567 self.assertLogFile(self.fn)
3568 rh.emit(self.next_rec())
3569 self.assertLogFile(self.fn + ".1")
3570 rh.emit(self.next_rec())
3571 self.assertLogFile(self.fn + ".2")
3572 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003573 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003574
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003575class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003576 # other test methods added below
3577 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003578 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3579 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003580 r = logging.makeLogRecord({'msg': 'testing'})
3581 fh.emit(r)
3582 self.assertLogFile(self.fn)
Vinay Sajipb7b19302011-05-24 07:12:41 +01003583 time.sleep(1.01) # just a little over a second ...
Vinay Sajipfd285022011-05-07 17:01:22 +01003584 fh.emit(r)
3585 fh.close()
3586 # At this point, we should have a recent rotated file which we
3587 # can test for the existence of. However, in practice, on some
3588 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003589 # in time to go to look for the log file. So, we go back a fair
3590 # bit, and stop as soon as we see a rotated file. In theory this
3591 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003592 found = False
3593 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003594 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003595 for secs in range(GO_BACK):
3596 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003597 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3598 found = os.path.exists(fn)
3599 if found:
3600 self.rmfiles.append(fn)
3601 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003602 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3603 if not found:
3604 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003605 dn, fn = os.path.split(self.fn)
3606 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003607 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3608 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajipfd285022011-05-07 17:01:22 +01003609 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003610
Vinay Sajip0372e102011-05-05 12:59:14 +01003611 def test_invalid(self):
3612 assertRaises = self.assertRaises
3613 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003614 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003615 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003616 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003617 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003618 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003619
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003620def secs(**kw):
3621 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3622
3623for when, exp in (('S', 1),
3624 ('M', 60),
3625 ('H', 60 * 60),
3626 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003627 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003628 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003629 ('W0', secs(days=4, hours=24)),
3630 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003631 def test_compute_rollover(self, when=when, exp=exp):
3632 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003633 self.fn, when=when, interval=1, backupCount=0, utc=True)
3634 currentTime = 0.0
3635 actual = rh.computeRollover(currentTime)
3636 if exp != actual:
3637 # Failures occur on some systems for MIDNIGHT and W0.
3638 # Print detailed calculation for MIDNIGHT so we can try to see
3639 # what's going on
3640 import time
3641 if when == 'MIDNIGHT':
3642 try:
3643 if rh.utc:
3644 t = time.gmtime(currentTime)
3645 else:
3646 t = time.localtime(currentTime)
3647 currentHour = t[3]
3648 currentMinute = t[4]
3649 currentSecond = t[5]
3650 # r is the number of seconds left between now and midnight
3651 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3652 currentMinute) * 60 +
3653 currentSecond)
3654 result = currentTime + r
3655 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3656 print('currentHour: %s' % currentHour, file=sys.stderr)
3657 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3658 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3659 print('r: %s' % r, file=sys.stderr)
3660 print('result: %s' % result, file=sys.stderr)
3661 except Exception:
3662 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3663 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003664 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003665 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3666
Vinay Sajip60ccd822011-05-09 17:32:09 +01003667
3668@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3669class NTEventLogHandlerTest(BaseTest):
3670 def test_basic(self):
3671 logtype = 'Application'
3672 elh = win32evtlog.OpenEventLog(None, logtype)
3673 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3674 h = logging.handlers.NTEventLogHandler('test_logging')
3675 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3676 h.handle(r)
3677 h.close()
3678 # Now see if the event is recorded
3679 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3680 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3681 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3682 found = False
3683 GO_BACK = 100
3684 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3685 for e in events:
3686 if e.SourceName != 'test_logging':
3687 continue
3688 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3689 if msg != 'Test Log Message\r\n':
3690 continue
3691 found = True
3692 break
3693 msg = 'Record not found in event log, went back %d records' % GO_BACK
3694 self.assertTrue(found, msg=msg)
3695
Christian Heimes180510d2008-03-03 19:15:45 +00003696# Set the locale to the platform-dependent default. I have no idea
3697# why the test does this, but in any case we save the current locale
3698# first and restore it at the end.
3699@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003700def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003701 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003702 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003703 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3704 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3705 ManagerTest, FormatterTest, BufferingFormatterTest,
3706 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3707 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3708 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3709 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003710 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003711 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003712 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003713 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003714
Christian Heimes180510d2008-03-03 19:15:45 +00003715if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003716 test_main()