blob: b0b8e19068917116332a5c0f9699f001a2f27dea [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajiped0473c2011-02-26 15:35:38 +00003# Copyright 2001-2011 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00004#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19"""Test harness for the logging module. Run all tests.
20
Vinay Sajiped0473c2011-02-26 15:35:38 +000021Copyright (C) 2001-2011 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Christian Heimes180510d2008-03-03 19:15:45 +000024import logging
25import logging.handlers
26import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000027
Benjamin Petersonf91df042009-02-13 02:50:59 +000028import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Christian Heimes180510d2008-03-03 19:15:45 +000030import pickle
31import io
32import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000033import json
Christian Heimes180510d2008-03-03 19:15:45 +000034import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000035import queue
Christian Heimes180510d2008-03-03 19:15:45 +000036import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000037import select
Christian Heimes180510d2008-03-03 19:15:45 +000038import socket
Christian Heimes180510d2008-03-03 19:15:45 +000039import struct
40import sys
41import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010042from test.support import captured_stdout, run_with_locale, run_unittest, patch
Vinay Sajipe723e962011-04-15 22:27:17 +010043from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000044import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010045import time
Christian Heimes180510d2008-03-03 19:15:45 +000046import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000047import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000048import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000049try:
50 import threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010051 # The following imports are needed only for tests which
52 import asynchat
53 import asyncore
54 import errno
55 from http.server import HTTPServer, BaseHTTPRequestHandler
56 import smtpd
57 from urllib.parse import urlparse, parse_qs
58 from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
59 ThreadingTCPServer, StreamRequestHandler)
Victor Stinner45df8202010-04-28 22:31:17 +000060except ImportError:
61 threading = None
Vinay Sajip60ccd822011-05-09 17:32:09 +010062try:
63 import win32evtlog
64except ImportError:
65 win32evtlog = None
66try:
67 import win32evtlogutil
68except ImportError:
69 win32evtlogutil = None
70 win32evtlog = None
Christian Heimes18c66892008-02-17 13:31:39 +000071
72
Christian Heimes180510d2008-03-03 19:15:45 +000073class BaseTest(unittest.TestCase):
74
75 """Base class for logging tests."""
76
77 log_format = "%(name)s -> %(levelname)s: %(message)s"
78 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
79 message_num = 0
80
81 def setUp(self):
82 """Setup the default logging stream to an internal StringIO instance,
83 so that we can examine log output as we want."""
84 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000085 logging._acquireLock()
86 try:
Christian Heimes180510d2008-03-03 19:15:45 +000087 self.saved_handlers = logging._handlers.copy()
88 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000089 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000090 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000091 self.logger_states = logger_states = {}
92 for name in saved_loggers:
93 logger_states[name] = getattr(saved_loggers[name],
94 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000095 finally:
96 logging._releaseLock()
97
Benjamin Peterson22005fc2010-04-11 16:25:06 +000098 # Set two unused loggers: one non-ASCII and one Unicode.
99 # This is to test correct operation when sorting existing
100 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +0000101 self.logger1 = logging.getLogger("\xab\xd7\xbb")
102 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000103
Christian Heimes180510d2008-03-03 19:15:45 +0000104 self.root_logger = logging.getLogger("")
105 self.original_logging_level = self.root_logger.getEffectiveLevel()
106
107 self.stream = io.StringIO()
108 self.root_logger.setLevel(logging.DEBUG)
109 self.root_hdlr = logging.StreamHandler(self.stream)
110 self.root_formatter = logging.Formatter(self.log_format)
111 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000112 if self.logger1.hasHandlers():
113 hlist = self.logger1.handlers + self.root_logger.handlers
114 raise AssertionError('Unexpected handlers: %s' % hlist)
115 if self.logger2.hasHandlers():
116 hlist = self.logger2.handlers + self.root_logger.handlers
117 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000118 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000119 self.assertTrue(self.logger1.hasHandlers())
120 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000121
122 def tearDown(self):
123 """Remove our logging stream, and restore the original logging
124 level."""
125 self.stream.close()
126 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000127 while self.root_logger.handlers:
128 h = self.root_logger.handlers[0]
129 self.root_logger.removeHandler(h)
130 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000131 self.root_logger.setLevel(self.original_logging_level)
132 logging._acquireLock()
133 try:
134 logging._levelNames.clear()
135 logging._levelNames.update(self.saved_level_names)
136 logging._handlers.clear()
137 logging._handlers.update(self.saved_handlers)
138 logging._handlerList[:] = self.saved_handler_list
139 loggerDict = logging.getLogger().manager.loggerDict
140 loggerDict.clear()
141 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000142 logger_states = self.logger_states
143 for name in self.logger_states:
144 if logger_states[name] is not None:
145 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000146 finally:
147 logging._releaseLock()
148
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000149 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000150 """Match the collected log lines against the regular expression
151 self.expected_log_pat, and compare the extracted group values to
152 the expected_values list of tuples."""
153 stream = stream or self.stream
154 pat = re.compile(self.expected_log_pat)
155 try:
156 stream.reset()
157 actual_lines = stream.readlines()
158 except AttributeError:
159 # StringIO.StringIO lacks a reset() method.
160 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100161 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000162 for actual, expected in zip(actual_lines, expected_values):
163 match = pat.search(actual)
164 if not match:
165 self.fail("Log line does not match expected pattern:\n" +
166 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000167 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000168 s = stream.read()
169 if s:
170 self.fail("Remaining output at end of log stream:\n" + s)
171
172 def next_message(self):
173 """Generate a message consisting solely of an auto-incrementing
174 integer."""
175 self.message_num += 1
176 return "%d" % self.message_num
177
178
179class BuiltinLevelsTest(BaseTest):
180 """Test builtin levels and their inheritance."""
181
182 def test_flat(self):
183 #Logging levels in a flat logger namespace.
184 m = self.next_message
185
186 ERR = logging.getLogger("ERR")
187 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000188 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000189 INF.setLevel(logging.INFO)
190 DEB = logging.getLogger("DEB")
191 DEB.setLevel(logging.DEBUG)
192
193 # These should log.
194 ERR.log(logging.CRITICAL, m())
195 ERR.error(m())
196
197 INF.log(logging.CRITICAL, m())
198 INF.error(m())
199 INF.warn(m())
200 INF.info(m())
201
202 DEB.log(logging.CRITICAL, m())
203 DEB.error(m())
204 DEB.warn (m())
205 DEB.info (m())
206 DEB.debug(m())
207
208 # These should not log.
209 ERR.warn(m())
210 ERR.info(m())
211 ERR.debug(m())
212
213 INF.debug(m())
214
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000215 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000216 ('ERR', 'CRITICAL', '1'),
217 ('ERR', 'ERROR', '2'),
218 ('INF', 'CRITICAL', '3'),
219 ('INF', 'ERROR', '4'),
220 ('INF', 'WARNING', '5'),
221 ('INF', 'INFO', '6'),
222 ('DEB', 'CRITICAL', '7'),
223 ('DEB', 'ERROR', '8'),
224 ('DEB', 'WARNING', '9'),
225 ('DEB', 'INFO', '10'),
226 ('DEB', 'DEBUG', '11'),
227 ])
228
229 def test_nested_explicit(self):
230 # Logging levels in a nested namespace, all explicitly set.
231 m = self.next_message
232
233 INF = logging.getLogger("INF")
234 INF.setLevel(logging.INFO)
235 INF_ERR = logging.getLogger("INF.ERR")
236 INF_ERR.setLevel(logging.ERROR)
237
238 # These should log.
239 INF_ERR.log(logging.CRITICAL, m())
240 INF_ERR.error(m())
241
242 # These should not log.
243 INF_ERR.warn(m())
244 INF_ERR.info(m())
245 INF_ERR.debug(m())
246
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000247 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000248 ('INF.ERR', 'CRITICAL', '1'),
249 ('INF.ERR', 'ERROR', '2'),
250 ])
251
252 def test_nested_inherited(self):
253 #Logging levels in a nested namespace, inherited from parent loggers.
254 m = self.next_message
255
256 INF = logging.getLogger("INF")
257 INF.setLevel(logging.INFO)
258 INF_ERR = logging.getLogger("INF.ERR")
259 INF_ERR.setLevel(logging.ERROR)
260 INF_UNDEF = logging.getLogger("INF.UNDEF")
261 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
262 UNDEF = logging.getLogger("UNDEF")
263
264 # These should log.
265 INF_UNDEF.log(logging.CRITICAL, m())
266 INF_UNDEF.error(m())
267 INF_UNDEF.warn(m())
268 INF_UNDEF.info(m())
269 INF_ERR_UNDEF.log(logging.CRITICAL, m())
270 INF_ERR_UNDEF.error(m())
271
272 # These should not log.
273 INF_UNDEF.debug(m())
274 INF_ERR_UNDEF.warn(m())
275 INF_ERR_UNDEF.info(m())
276 INF_ERR_UNDEF.debug(m())
277
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000278 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000279 ('INF.UNDEF', 'CRITICAL', '1'),
280 ('INF.UNDEF', 'ERROR', '2'),
281 ('INF.UNDEF', 'WARNING', '3'),
282 ('INF.UNDEF', 'INFO', '4'),
283 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
284 ('INF.ERR.UNDEF', 'ERROR', '6'),
285 ])
286
287 def test_nested_with_virtual_parent(self):
288 # Logging levels when some parent does not exist yet.
289 m = self.next_message
290
291 INF = logging.getLogger("INF")
292 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
293 CHILD = logging.getLogger("INF.BADPARENT")
294 INF.setLevel(logging.INFO)
295
296 # These should log.
297 GRANDCHILD.log(logging.FATAL, m())
298 GRANDCHILD.info(m())
299 CHILD.log(logging.FATAL, m())
300 CHILD.info(m())
301
302 # These should not log.
303 GRANDCHILD.debug(m())
304 CHILD.debug(m())
305
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000306 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000307 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
308 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
309 ('INF.BADPARENT', 'CRITICAL', '3'),
310 ('INF.BADPARENT', 'INFO', '4'),
311 ])
312
313
314class BasicFilterTest(BaseTest):
315
316 """Test the bundled Filter class."""
317
318 def test_filter(self):
319 # Only messages satisfying the specified criteria pass through the
320 # filter.
321 filter_ = logging.Filter("spam.eggs")
322 handler = self.root_logger.handlers[0]
323 try:
324 handler.addFilter(filter_)
325 spam = logging.getLogger("spam")
326 spam_eggs = logging.getLogger("spam.eggs")
327 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
328 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
329
330 spam.info(self.next_message())
331 spam_eggs.info(self.next_message()) # Good.
332 spam_eggs_fish.info(self.next_message()) # Good.
333 spam_bakedbeans.info(self.next_message())
334
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000335 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000336 ('spam.eggs', 'INFO', '2'),
337 ('spam.eggs.fish', 'INFO', '3'),
338 ])
339 finally:
340 handler.removeFilter(filter_)
341
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000342 def test_callable_filter(self):
343 # Only messages satisfying the specified criteria pass through the
344 # filter.
345
346 def filterfunc(record):
347 parts = record.name.split('.')
348 prefix = '.'.join(parts[:2])
349 return prefix == 'spam.eggs'
350
351 handler = self.root_logger.handlers[0]
352 try:
353 handler.addFilter(filterfunc)
354 spam = logging.getLogger("spam")
355 spam_eggs = logging.getLogger("spam.eggs")
356 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
357 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
358
359 spam.info(self.next_message())
360 spam_eggs.info(self.next_message()) # Good.
361 spam_eggs_fish.info(self.next_message()) # Good.
362 spam_bakedbeans.info(self.next_message())
363
364 self.assert_log_lines([
365 ('spam.eggs', 'INFO', '2'),
366 ('spam.eggs.fish', 'INFO', '3'),
367 ])
368 finally:
369 handler.removeFilter(filterfunc)
370
Vinay Sajip985ef872011-04-26 19:34:04 +0100371 def test_empty_filter(self):
372 f = logging.Filter()
373 r = logging.makeLogRecord({'name': 'spam.eggs'})
374 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000375
376#
377# First, we define our levels. There can be as many as you want - the only
378# limitations are that they should be integers, the lowest should be > 0 and
379# larger values mean less information being logged. If you need specific
380# level values which do not fit into these limitations, you can use a
381# mapping dictionary to convert between your application levels and the
382# logging system.
383#
384SILENT = 120
385TACITURN = 119
386TERSE = 118
387EFFUSIVE = 117
388SOCIABLE = 116
389VERBOSE = 115
390TALKATIVE = 114
391GARRULOUS = 113
392CHATTERBOX = 112
393BORING = 111
394
395LEVEL_RANGE = range(BORING, SILENT + 1)
396
397#
398# Next, we define names for our levels. You don't need to do this - in which
399# case the system will use "Level n" to denote the text for the level.
400#
401my_logging_levels = {
402 SILENT : 'Silent',
403 TACITURN : 'Taciturn',
404 TERSE : 'Terse',
405 EFFUSIVE : 'Effusive',
406 SOCIABLE : 'Sociable',
407 VERBOSE : 'Verbose',
408 TALKATIVE : 'Talkative',
409 GARRULOUS : 'Garrulous',
410 CHATTERBOX : 'Chatterbox',
411 BORING : 'Boring',
412}
413
414class GarrulousFilter(logging.Filter):
415
416 """A filter which blocks garrulous messages."""
417
418 def filter(self, record):
419 return record.levelno != GARRULOUS
420
421class VerySpecificFilter(logging.Filter):
422
423 """A filter which blocks sociable and taciturn messages."""
424
425 def filter(self, record):
426 return record.levelno not in [SOCIABLE, TACITURN]
427
428
429class CustomLevelsAndFiltersTest(BaseTest):
430
431 """Test various filtering possibilities with custom logging levels."""
432
433 # Skip the logger name group.
434 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
435
436 def setUp(self):
437 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000438 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000439 logging.addLevelName(k, v)
440
441 def log_at_all_levels(self, logger):
442 for lvl in LEVEL_RANGE:
443 logger.log(lvl, self.next_message())
444
445 def test_logger_filter(self):
446 # Filter at logger level.
447 self.root_logger.setLevel(VERBOSE)
448 # Levels >= 'Verbose' are good.
449 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000450 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000451 ('Verbose', '5'),
452 ('Sociable', '6'),
453 ('Effusive', '7'),
454 ('Terse', '8'),
455 ('Taciturn', '9'),
456 ('Silent', '10'),
457 ])
458
459 def test_handler_filter(self):
460 # Filter at handler level.
461 self.root_logger.handlers[0].setLevel(SOCIABLE)
462 try:
463 # Levels >= 'Sociable' are good.
464 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000465 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000466 ('Sociable', '6'),
467 ('Effusive', '7'),
468 ('Terse', '8'),
469 ('Taciturn', '9'),
470 ('Silent', '10'),
471 ])
472 finally:
473 self.root_logger.handlers[0].setLevel(logging.NOTSET)
474
475 def test_specific_filters(self):
476 # Set a specific filter object on the handler, and then add another
477 # filter object on the logger itself.
478 handler = self.root_logger.handlers[0]
479 specific_filter = None
480 garr = GarrulousFilter()
481 handler.addFilter(garr)
482 try:
483 self.log_at_all_levels(self.root_logger)
484 first_lines = [
485 # Notice how 'Garrulous' is missing
486 ('Boring', '1'),
487 ('Chatterbox', '2'),
488 ('Talkative', '4'),
489 ('Verbose', '5'),
490 ('Sociable', '6'),
491 ('Effusive', '7'),
492 ('Terse', '8'),
493 ('Taciturn', '9'),
494 ('Silent', '10'),
495 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000496 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000497
498 specific_filter = VerySpecificFilter()
499 self.root_logger.addFilter(specific_filter)
500 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000501 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000502 # Not only 'Garrulous' is still missing, but also 'Sociable'
503 # and 'Taciturn'
504 ('Boring', '11'),
505 ('Chatterbox', '12'),
506 ('Talkative', '14'),
507 ('Verbose', '15'),
508 ('Effusive', '17'),
509 ('Terse', '18'),
510 ('Silent', '20'),
511 ])
512 finally:
513 if specific_filter:
514 self.root_logger.removeFilter(specific_filter)
515 handler.removeFilter(garr)
516
517
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100518class HandlerTest(BaseTest):
519 def test_name(self):
520 h = logging.Handler()
521 h.name = 'generic'
522 self.assertEqual(h.name, 'generic')
523 h.name = 'anothergeneric'
524 self.assertEqual(h.name, 'anothergeneric')
525 self.assertRaises(NotImplementedError, h.emit, None)
526
Vinay Sajip5a35b062011-04-27 11:31:14 +0100527 def test_builtin_handlers(self):
528 # We can't actually *use* too many handlers in the tests,
529 # but we can try instantiating them with various options
530 if sys.platform in ('linux2', 'darwin'):
531 for existing in (True, False):
532 fd, fn = tempfile.mkstemp()
533 os.close(fd)
534 if not existing:
535 os.unlink(fn)
536 h = logging.handlers.WatchedFileHandler(fn, delay=True)
537 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100538 dev, ino = h.dev, h.ino
539 self.assertNotEqual(dev, -1)
540 self.assertNotEqual(ino, -1)
541 r = logging.makeLogRecord({'msg': 'Test'})
542 h.handle(r)
543 # Now remove the file.
544 os.unlink(fn)
545 self.assertFalse(os.path.exists(fn))
546 # The next call should recreate the file.
547 h.handle(r)
548 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100549 else:
550 self.assertEqual(h.dev, -1)
551 self.assertEqual(h.ino, -1)
552 h.close()
553 if existing:
554 os.unlink(fn)
555 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100556 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100557 else:
558 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100559 try:
560 h = logging.handlers.SysLogHandler(sockname)
561 self.assertEqual(h.facility, h.LOG_USER)
562 self.assertTrue(h.unixsocket)
563 h.close()
564 except socket.error: # syslogd might not be available
565 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100566 for method in ('GET', 'POST', 'PUT'):
567 if method == 'PUT':
568 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
569 'localhost', '/log', method)
570 else:
571 h = logging.handlers.HTTPHandler('localhost', '/log', method)
572 h.close()
573 h = logging.handlers.BufferingHandler(0)
574 r = logging.makeLogRecord({})
575 self.assertTrue(h.shouldFlush(r))
576 h.close()
577 h = logging.handlers.BufferingHandler(1)
578 self.assertFalse(h.shouldFlush(r))
579 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100580
581class BadStream(object):
582 def write(self, data):
583 raise RuntimeError('deliberate mistake')
584
585class TestStreamHandler(logging.StreamHandler):
586 def handleError(self, record):
587 self.error_record = record
588
589class StreamHandlerTest(BaseTest):
590 def test_error_handling(self):
591 h = TestStreamHandler(BadStream())
592 r = logging.makeLogRecord({})
593 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100594 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100595 try:
596 h.handle(r)
597 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100598 h = logging.StreamHandler(BadStream())
599 sys.stderr = sio = io.StringIO()
600 h.handle(r)
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100601 self.assertIn('\nRuntimeError: deliberate mistake\n',
602 sio.getvalue())
Vinay Sajip985ef872011-04-26 19:34:04 +0100603 logging.raiseExceptions = False
604 sys.stderr = sio = io.StringIO()
605 h.handle(r)
606 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100607 finally:
608 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100609 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100610
Vinay Sajip7367d082011-05-02 13:17:27 +0100611# -- The following section could be moved into a server_helper.py module
612# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100613
Vinay Sajipce7c9782011-05-17 07:15:53 +0100614if threading:
615 class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100616 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100617 This derived class has had to be created because smtpd does not
618 support use of custom channel maps, although they are allowed by
619 asyncore's design. Issue #11959 has been raised to address this,
620 and if resolved satisfactorily, some of this code can be removed.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100621 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100622 def __init__(self, server, conn, addr, sockmap):
623 asynchat.async_chat.__init__(self, conn, sockmap)
624 self.smtp_server = server
625 self.conn = conn
626 self.addr = addr
627 self.received_lines = []
628 self.smtp_state = self.COMMAND
629 self.seen_greeting = ''
630 self.mailfrom = None
631 self.rcpttos = []
632 self.received_data = ''
633 self.fqdn = socket.getfqdn()
634 self.num_bytes = 0
635 try:
636 self.peer = conn.getpeername()
637 except socket.error as err:
638 # a race condition may occur if the other end is closing
639 # before we can get the peername
640 self.close()
641 if err.args[0] != errno.ENOTCONN:
642 raise
643 return
644 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
645 self.set_terminator(b'\r\n')
Vinay Sajipa463d252011-04-30 21:52:48 +0100646
Vinay Sajip314b92b2011-05-02 14:31:16 +0100647
Vinay Sajipce7c9782011-05-17 07:15:53 +0100648 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100649 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100650 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100651
Vinay Sajipce7c9782011-05-17 07:15:53 +0100652 :param addr: A (host, port) tuple which the server listens on.
653 You can specify a port value of zero: the server's
654 *port* attribute will hold the actual port number
655 used, which can be used in client connections.
656 :param handler: A callable which will be called to process
657 incoming messages. The handler will be passed
658 the client address tuple, who the message is from,
659 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100660 :param poll_interval: The interval, in seconds, used in the underlying
661 :func:`select` or :func:`poll` call by
662 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100663 :param sockmap: A dictionary which will be used to hold
664 :class:`asyncore.dispatcher` instances used by
665 :func:`asyncore.loop`. This avoids changing the
666 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100667 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100668 channel_class = TestSMTPChannel
669
670 def __init__(self, addr, handler, poll_interval, sockmap):
671 self._localaddr = addr
672 self._remoteaddr = None
673 self.sockmap = sockmap
674 asyncore.dispatcher.__init__(self, map=sockmap)
675 try:
676 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
677 sock.setblocking(0)
678 self.set_socket(sock, map=sockmap)
679 # try to re-use a server port if possible
680 self.set_reuse_addr()
681 self.bind(addr)
682 self.port = sock.getsockname()[1]
683 self.listen(5)
684 except:
685 self.close()
Vinay Sajipe73afad2011-05-10 07:48:28 +0100686 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +0100687 self._handler = handler
688 self._thread = None
689 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100690
Vinay Sajipce7c9782011-05-17 07:15:53 +0100691 def handle_accepted(self, conn, addr):
692 """
693 Redefined only because the base class does not pass in a
694 map, forcing use of a global in :mod:`asyncore`.
695 """
696 channel = self.channel_class(self, conn, addr, self.sockmap)
Vinay Sajip314b92b2011-05-02 14:31:16 +0100697
Vinay Sajipce7c9782011-05-17 07:15:53 +0100698 def process_message(self, peer, mailfrom, rcpttos, data):
699 """
700 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100701
Vinay Sajipce7c9782011-05-17 07:15:53 +0100702 Typically, this will be a test case method.
703 :param peer: The client (host, port) tuple.
704 :param mailfrom: The address of the sender.
705 :param rcpttos: The addresses of the recipients.
706 :param data: The message.
707 """
708 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100709
Vinay Sajipce7c9782011-05-17 07:15:53 +0100710 def start(self):
711 """
712 Start the server running on a separate daemon thread.
713 """
714 self._thread = t = threading.Thread(target=self.serve_forever,
715 args=(self.poll_interval,))
716 t.setDaemon(True)
717 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100718
Vinay Sajipce7c9782011-05-17 07:15:53 +0100719 def serve_forever(self, poll_interval):
720 """
721 Run the :mod:`asyncore` loop until normal termination
722 conditions arise.
723 :param poll_interval: The interval, in seconds, used in the underlying
724 :func:`select` or :func:`poll` call by
725 :func:`asyncore.loop`.
726 """
727 try:
728 asyncore.loop(poll_interval, map=self.sockmap)
729 except select.error:
730 # On FreeBSD 8, closing the server repeatably
731 # raises this error. We swallow it if the
732 # server has been closed.
733 if self.connected or self.accepting:
734 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100735
Vinay Sajipce7c9782011-05-17 07:15:53 +0100736 def stop(self, timeout=None):
737 """
738 Stop the thread by closing the server instance.
739 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100740
Vinay Sajipce7c9782011-05-17 07:15:53 +0100741 :param timeout: How long to wait for the server thread
742 to terminate.
743 """
744 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100745 self._thread.join(timeout)
746 self._thread = None
Vinay Sajip7367d082011-05-02 13:17:27 +0100747
Vinay Sajipce7c9782011-05-17 07:15:53 +0100748 class ControlMixin(object):
749 """
750 This mixin is used to start a server on a separate thread, and
751 shut it down programmatically. Request handling is simplified - instead
752 of needing to derive a suitable RequestHandler subclass, you just
753 provide a callable which will be passed each received request to be
754 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100755
Vinay Sajipce7c9782011-05-17 07:15:53 +0100756 :param handler: A handler callable which will be called with a
757 single parameter - the request - in order to
758 process the request. This handler is called on the
759 server thread, effectively meaning that requests are
760 processed serially. While not quite Web scale ;-),
761 this should be fine for testing applications.
762 :param poll_interval: The polling interval in seconds.
763 """
764 def __init__(self, handler, poll_interval):
765 self._thread = None
766 self.poll_interval = poll_interval
767 self._handler = handler
768 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100769
Vinay Sajipce7c9782011-05-17 07:15:53 +0100770 def start(self):
771 """
772 Create a daemon thread to run the server, and start it.
773 """
774 self._thread = t = threading.Thread(target=self.serve_forever,
775 args=(self.poll_interval,))
776 t.setDaemon(True)
777 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100778
Vinay Sajipce7c9782011-05-17 07:15:53 +0100779 def serve_forever(self, poll_interval):
780 """
781 Run the server. Set the ready flag before entering the
782 service loop.
783 """
784 self.ready.set()
785 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100786
Vinay Sajipce7c9782011-05-17 07:15:53 +0100787 def stop(self, timeout=None):
788 """
789 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100790
Vinay Sajipce7c9782011-05-17 07:15:53 +0100791 :param timeout: How long to wait for the server thread
792 to terminate.
793 """
794 self.shutdown()
795 if self._thread is not None:
796 self._thread.join(timeout)
797 self._thread = None
798 self.server_close()
799 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100800
Vinay Sajipce7c9782011-05-17 07:15:53 +0100801 class TestHTTPServer(ControlMixin, HTTPServer):
802 """
803 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100804
Vinay Sajipce7c9782011-05-17 07:15:53 +0100805 :param addr: A tuple with the IP address and port to listen on.
806 :param handler: A handler callable which will be called with a
807 single parameter - the request - in order to
808 process the request.
809 :param poll_interval: The polling interval in seconds.
810 :param log: Pass ``True`` to enable log messages.
811 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100812 def __init__(self, addr, handler, poll_interval=0.5,
813 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100814 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
815 def __getattr__(self, name, default=None):
816 if name.startswith('do_'):
817 return self.process_request
818 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100819
Vinay Sajipce7c9782011-05-17 07:15:53 +0100820 def process_request(self):
821 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100822
Vinay Sajipce7c9782011-05-17 07:15:53 +0100823 def log_message(self, format, *args):
824 if log:
825 super(DelegatingHTTPRequestHandler,
826 self).log_message(format, *args)
827 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
828 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100829 self.sslctx = sslctx
830
831 def get_request(self):
832 try:
833 sock, addr = self.socket.accept()
834 if self.sslctx:
835 sock = self.sslctx.wrap_socket(sock, server_side=True)
836 except socket.error as e:
837 # socket errors are silenced by the caller, print them here
838 sys.stderr.write("Got an error:\n%s\n" % e)
839 raise
840 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100841
Vinay Sajipce7c9782011-05-17 07:15:53 +0100842 class TestTCPServer(ControlMixin, ThreadingTCPServer):
843 """
844 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100845
Vinay Sajipce7c9782011-05-17 07:15:53 +0100846 :param addr: A tuple with the IP address and port to listen on.
847 :param handler: A handler callable which will be called with a single
848 parameter - the request - in order to process the request.
849 :param poll_interval: The polling interval in seconds.
850 :bind_and_activate: If True (the default), binds the server and starts it
851 listening. If False, you need to call
852 :meth:`server_bind` and :meth:`server_activate` at
853 some later time before calling :meth:`start`, so that
854 the server will set up the socket and listen on it.
855 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100856
Vinay Sajipce7c9782011-05-17 07:15:53 +0100857 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100858
Vinay Sajipce7c9782011-05-17 07:15:53 +0100859 def __init__(self, addr, handler, poll_interval=0.5,
860 bind_and_activate=True):
861 class DelegatingTCPRequestHandler(StreamRequestHandler):
862
863 def handle(self):
864 self.server._handler(self)
865 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
866 bind_and_activate)
867 ControlMixin.__init__(self, handler, poll_interval)
868
869 def server_bind(self):
870 super(TestTCPServer, self).server_bind()
871 self.port = self.socket.getsockname()[1]
872
873 class TestUDPServer(ControlMixin, ThreadingUDPServer):
874 """
875 A UDP server which is controllable using :class:`ControlMixin`.
876
877 :param addr: A tuple with the IP address and port to listen on.
878 :param handler: A handler callable which will be called with a
879 single parameter - the request - in order to
880 process the request.
881 :param poll_interval: The polling interval for shutdown requests,
882 in seconds.
883 :bind_and_activate: If True (the default), binds the server and
884 starts it listening. If False, you need to
885 call :meth:`server_bind` and
886 :meth:`server_activate` at some later time
887 before calling :meth:`start`, so that the server will
888 set up the socket and listen on it.
889 """
Vinay Sajip3ef12292011-05-23 23:00:42 +0100890 def __init__(self, addr, handler, poll_interval=0.5,
891 bind_and_activate=True):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100892 class DelegatingUDPRequestHandler(DatagramRequestHandler):
893
894 def handle(self):
895 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100896
897 def finish(self):
898 data = self.wfile.getvalue()
Vinay Sajip3ef12292011-05-23 23:00:42 +0100899 if data:
900 try:
901 super(DelegatingUDPRequestHandler, self).finish()
902 except socket.error:
903 if not self.server._closed:
904 raise
Vinay Sajipba980db2011-05-23 21:37:54 +0100905
Vinay Sajip3ef12292011-05-23 23:00:42 +0100906 ThreadingUDPServer.__init__(self, addr,
907 DelegatingUDPRequestHandler,
Vinay Sajipce7c9782011-05-17 07:15:53 +0100908 bind_and_activate)
909 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100910 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100911
912 def server_bind(self):
913 super(TestUDPServer, self).server_bind()
914 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100915
Vinay Sajipba980db2011-05-23 21:37:54 +0100916 def server_close(self):
917 super(TestUDPServer, self).server_close()
918 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100919
920# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100921
Vinay Sajipce7c9782011-05-17 07:15:53 +0100922@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100923class SMTPHandlerTest(BaseTest):
924 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100925 sockmap = {}
926 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
927 sockmap)
928 server.start()
929 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100930 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
931 self.assertEqual(h.toaddrs, ['you'])
932 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100933 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100934 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100935 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100936 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100937 server.stop()
938 self.assertEqual(len(self.messages), 1)
939 peer, mailfrom, rcpttos, data = self.messages[0]
940 self.assertEqual(mailfrom, 'me')
941 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100942 self.assertIn('\nSubject: Log\n', data)
Vinay Sajipa463d252011-04-30 21:52:48 +0100943 self.assertTrue(data.endswith('\n\nHello'))
944 h.close()
945
946 def process_message(self, *args):
947 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100948 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100949
Christian Heimes180510d2008-03-03 19:15:45 +0000950class MemoryHandlerTest(BaseTest):
951
952 """Tests for the MemoryHandler."""
953
954 # Do not bother with a logger name group.
955 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
956
957 def setUp(self):
958 BaseTest.setUp(self)
959 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
960 self.root_hdlr)
961 self.mem_logger = logging.getLogger('mem')
962 self.mem_logger.propagate = 0
963 self.mem_logger.addHandler(self.mem_hdlr)
964
965 def tearDown(self):
966 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000967 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000968
969 def test_flush(self):
970 # The memory handler flushes to its target handler based on specific
971 # criteria (message count and message level).
972 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000973 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000974 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000975 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000976 # This will flush because the level is >= logging.WARNING
977 self.mem_logger.warn(self.next_message())
978 lines = [
979 ('DEBUG', '1'),
980 ('INFO', '2'),
981 ('WARNING', '3'),
982 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000983 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000984 for n in (4, 14):
985 for i in range(9):
986 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000987 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000988 # This will flush because it's the 10th message since the last
989 # flush.
990 self.mem_logger.debug(self.next_message())
991 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000992 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000993
994 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000995 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000996
997
998class ExceptionFormatter(logging.Formatter):
999 """A special exception formatter."""
1000 def formatException(self, ei):
1001 return "Got a [%s]" % ei[0].__name__
1002
1003
1004class ConfigFileTest(BaseTest):
1005
1006 """Reading logging config from a .ini-style config file."""
1007
1008 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1009
1010 # config0 is a standard configuration.
1011 config0 = """
1012 [loggers]
1013 keys=root
1014
1015 [handlers]
1016 keys=hand1
1017
1018 [formatters]
1019 keys=form1
1020
1021 [logger_root]
1022 level=WARNING
1023 handlers=hand1
1024
1025 [handler_hand1]
1026 class=StreamHandler
1027 level=NOTSET
1028 formatter=form1
1029 args=(sys.stdout,)
1030
1031 [formatter_form1]
1032 format=%(levelname)s ++ %(message)s
1033 datefmt=
1034 """
1035
1036 # config1 adds a little to the standard configuration.
1037 config1 = """
1038 [loggers]
1039 keys=root,parser
1040
1041 [handlers]
1042 keys=hand1
1043
1044 [formatters]
1045 keys=form1
1046
1047 [logger_root]
1048 level=WARNING
1049 handlers=
1050
1051 [logger_parser]
1052 level=DEBUG
1053 handlers=hand1
1054 propagate=1
1055 qualname=compiler.parser
1056
1057 [handler_hand1]
1058 class=StreamHandler
1059 level=NOTSET
1060 formatter=form1
1061 args=(sys.stdout,)
1062
1063 [formatter_form1]
1064 format=%(levelname)s ++ %(message)s
1065 datefmt=
1066 """
1067
Vinay Sajip3f84b072011-03-07 17:49:33 +00001068 # config1a moves the handler to the root.
1069 config1a = """
1070 [loggers]
1071 keys=root,parser
1072
1073 [handlers]
1074 keys=hand1
1075
1076 [formatters]
1077 keys=form1
1078
1079 [logger_root]
1080 level=WARNING
1081 handlers=hand1
1082
1083 [logger_parser]
1084 level=DEBUG
1085 handlers=
1086 propagate=1
1087 qualname=compiler.parser
1088
1089 [handler_hand1]
1090 class=StreamHandler
1091 level=NOTSET
1092 formatter=form1
1093 args=(sys.stdout,)
1094
1095 [formatter_form1]
1096 format=%(levelname)s ++ %(message)s
1097 datefmt=
1098 """
1099
Christian Heimes180510d2008-03-03 19:15:45 +00001100 # config2 has a subtle configuration error that should be reported
1101 config2 = config1.replace("sys.stdout", "sys.stbout")
1102
1103 # config3 has a less subtle configuration error
1104 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1105
1106 # config4 specifies a custom formatter class to be loaded
1107 config4 = """
1108 [loggers]
1109 keys=root
1110
1111 [handlers]
1112 keys=hand1
1113
1114 [formatters]
1115 keys=form1
1116
1117 [logger_root]
1118 level=NOTSET
1119 handlers=hand1
1120
1121 [handler_hand1]
1122 class=StreamHandler
1123 level=NOTSET
1124 formatter=form1
1125 args=(sys.stdout,)
1126
1127 [formatter_form1]
1128 class=""" + __name__ + """.ExceptionFormatter
1129 format=%(levelname)s:%(name)s:%(message)s
1130 datefmt=
1131 """
1132
Georg Brandl3dbca812008-07-23 16:10:53 +00001133 # config5 specifies a custom handler class to be loaded
1134 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1135
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001136 # config6 uses ', ' delimiters in the handlers and formatters sections
1137 config6 = """
1138 [loggers]
1139 keys=root,parser
1140
1141 [handlers]
1142 keys=hand1, hand2
1143
1144 [formatters]
1145 keys=form1, form2
1146
1147 [logger_root]
1148 level=WARNING
1149 handlers=
1150
1151 [logger_parser]
1152 level=DEBUG
1153 handlers=hand1
1154 propagate=1
1155 qualname=compiler.parser
1156
1157 [handler_hand1]
1158 class=StreamHandler
1159 level=NOTSET
1160 formatter=form1
1161 args=(sys.stdout,)
1162
1163 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001164 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001165 level=NOTSET
1166 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001167 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001168
1169 [formatter_form1]
1170 format=%(levelname)s ++ %(message)s
1171 datefmt=
1172
1173 [formatter_form2]
1174 format=%(message)s
1175 datefmt=
1176 """
1177
Vinay Sajip3f84b072011-03-07 17:49:33 +00001178 # config7 adds a compiler logger.
1179 config7 = """
1180 [loggers]
1181 keys=root,parser,compiler
1182
1183 [handlers]
1184 keys=hand1
1185
1186 [formatters]
1187 keys=form1
1188
1189 [logger_root]
1190 level=WARNING
1191 handlers=hand1
1192
1193 [logger_compiler]
1194 level=DEBUG
1195 handlers=
1196 propagate=1
1197 qualname=compiler
1198
1199 [logger_parser]
1200 level=DEBUG
1201 handlers=
1202 propagate=1
1203 qualname=compiler.parser
1204
1205 [handler_hand1]
1206 class=StreamHandler
1207 level=NOTSET
1208 formatter=form1
1209 args=(sys.stdout,)
1210
1211 [formatter_form1]
1212 format=%(levelname)s ++ %(message)s
1213 datefmt=
1214 """
1215
Christian Heimes180510d2008-03-03 19:15:45 +00001216 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001217 file = io.StringIO(textwrap.dedent(conf))
1218 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001219
1220 def test_config0_ok(self):
1221 # A simple config file which overrides the default settings.
1222 with captured_stdout() as output:
1223 self.apply_config(self.config0)
1224 logger = logging.getLogger()
1225 # Won't output anything
1226 logger.info(self.next_message())
1227 # Outputs a message
1228 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001229 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001230 ('ERROR', '2'),
1231 ], stream=output)
1232 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001233 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001234
Georg Brandl3dbca812008-07-23 16:10:53 +00001235 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001236 # A config file defining a sub-parser as well.
1237 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001238 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001239 logger = logging.getLogger("compiler.parser")
1240 # Both will output a message
1241 logger.info(self.next_message())
1242 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001243 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001244 ('INFO', '1'),
1245 ('ERROR', '2'),
1246 ], stream=output)
1247 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001248 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001249
1250 def test_config2_failure(self):
1251 # A simple config file which overrides the default settings.
1252 self.assertRaises(Exception, self.apply_config, self.config2)
1253
1254 def test_config3_failure(self):
1255 # A simple config file which overrides the default settings.
1256 self.assertRaises(Exception, self.apply_config, self.config3)
1257
1258 def test_config4_ok(self):
1259 # A config file specifying a custom formatter class.
1260 with captured_stdout() as output:
1261 self.apply_config(self.config4)
1262 logger = logging.getLogger()
1263 try:
1264 raise RuntimeError()
1265 except RuntimeError:
1266 logging.exception("just testing")
1267 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001268 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001269 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1270 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001271 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001272
Georg Brandl3dbca812008-07-23 16:10:53 +00001273 def test_config5_ok(self):
1274 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001275
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001276 def test_config6_ok(self):
1277 self.test_config1_ok(config=self.config6)
1278
Vinay Sajip3f84b072011-03-07 17:49:33 +00001279 def test_config7_ok(self):
1280 with captured_stdout() as output:
1281 self.apply_config(self.config1a)
1282 logger = logging.getLogger("compiler.parser")
1283 # See issue #11424. compiler-hyphenated sorts
1284 # between compiler and compiler.xyz and this
1285 # was preventing compiler.xyz from being included
1286 # in the child loggers of compiler because of an
1287 # overzealous loop termination condition.
1288 hyphenated = logging.getLogger('compiler-hyphenated')
1289 # All will output a message
1290 logger.info(self.next_message())
1291 logger.error(self.next_message())
1292 hyphenated.critical(self.next_message())
1293 self.assert_log_lines([
1294 ('INFO', '1'),
1295 ('ERROR', '2'),
1296 ('CRITICAL', '3'),
1297 ], stream=output)
1298 # Original logger output is empty.
1299 self.assert_log_lines([])
1300 with captured_stdout() as output:
1301 self.apply_config(self.config7)
1302 logger = logging.getLogger("compiler.parser")
1303 self.assertFalse(logger.disabled)
1304 # Both will output a message
1305 logger.info(self.next_message())
1306 logger.error(self.next_message())
1307 logger = logging.getLogger("compiler.lexer")
1308 # Both will output a message
1309 logger.info(self.next_message())
1310 logger.error(self.next_message())
1311 # Will not appear
1312 hyphenated.critical(self.next_message())
1313 self.assert_log_lines([
1314 ('INFO', '4'),
1315 ('ERROR', '5'),
1316 ('INFO', '6'),
1317 ('ERROR', '7'),
1318 ], stream=output)
1319 # Original logger output is empty.
1320 self.assert_log_lines([])
1321
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001322
Victor Stinner45df8202010-04-28 22:31:17 +00001323@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001324class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001325
Christian Heimes180510d2008-03-03 19:15:45 +00001326 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001327
Christian Heimes180510d2008-03-03 19:15:45 +00001328 def setUp(self):
1329 """Set up a TCP server to receive log messages, and a SocketHandler
1330 pointing to that server's address and port."""
1331 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001332 addr = ('localhost', 0)
1333 self.server = server = TestTCPServer(addr, self.handle_socket,
1334 0.01)
1335 server.start()
1336 server.ready.wait()
1337 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1338 server.port)
1339 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001340 self.root_logger.removeHandler(self.root_logger.handlers[0])
1341 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001342 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001343
Christian Heimes180510d2008-03-03 19:15:45 +00001344 def tearDown(self):
1345 """Shutdown the TCP server."""
1346 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001347 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001348 self.root_logger.removeHandler(self.sock_hdlr)
1349 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001350 finally:
1351 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001352
Vinay Sajip7367d082011-05-02 13:17:27 +01001353 def handle_socket(self, request):
1354 conn = request.connection
1355 while True:
1356 chunk = conn.recv(4)
1357 if len(chunk) < 4:
1358 break
1359 slen = struct.unpack(">L", chunk)[0]
1360 chunk = conn.recv(slen)
1361 while len(chunk) < slen:
1362 chunk = chunk + conn.recv(slen - len(chunk))
1363 obj = pickle.loads(chunk)
1364 record = logging.makeLogRecord(obj)
1365 self.log_output += record.msg + '\n'
1366 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001367
Christian Heimes180510d2008-03-03 19:15:45 +00001368 def test_output(self):
1369 # The log message sent to the SocketHandler is properly received.
1370 logger = logging.getLogger("tcp")
1371 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001372 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001373 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001374 self.handled.acquire()
1375 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001376
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001377 def test_noserver(self):
1378 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001379 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001380 #The logging call should try to connect, which should fail
1381 try:
1382 raise RuntimeError('Deliberate mistake')
1383 except RuntimeError:
1384 self.root_logger.exception('Never sent')
1385 self.root_logger.error('Never sent, either')
1386 now = time.time()
1387 self.assertTrue(self.sock_hdlr.retryTime > now)
1388 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1389 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001390
Vinay Sajip7367d082011-05-02 13:17:27 +01001391
1392@unittest.skipUnless(threading, 'Threading required for this test.')
1393class DatagramHandlerTest(BaseTest):
1394
1395 """Test for DatagramHandler."""
1396
1397 def setUp(self):
1398 """Set up a UDP server to receive log messages, and a DatagramHandler
1399 pointing to that server's address and port."""
1400 BaseTest.setUp(self)
1401 addr = ('localhost', 0)
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001402 self.server = server = TestUDPServer(addr, self.handle_datagram, 0.01)
Vinay Sajip7367d082011-05-02 13:17:27 +01001403 server.start()
1404 server.ready.wait()
1405 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1406 server.port)
1407 self.log_output = ''
1408 self.root_logger.removeHandler(self.root_logger.handlers[0])
1409 self.root_logger.addHandler(self.sock_hdlr)
1410 self.handled = threading.Event()
1411
1412 def tearDown(self):
1413 """Shutdown the UDP server."""
1414 try:
1415 self.server.stop(2.0)
1416 self.root_logger.removeHandler(self.sock_hdlr)
1417 self.sock_hdlr.close()
1418 finally:
1419 BaseTest.tearDown(self)
1420
1421 def handle_datagram(self, request):
1422 slen = struct.pack('>L', 0) # length of prefix
1423 packet = request.packet[len(slen):]
1424 obj = pickle.loads(packet)
1425 record = logging.makeLogRecord(obj)
1426 self.log_output += record.msg + '\n'
1427 self.handled.set()
1428
1429 def test_output(self):
1430 # The log message sent to the DatagramHandler is properly received.
1431 logger = logging.getLogger("udp")
1432 logger.error("spam")
1433 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001434 self.handled.clear()
1435 logger.error("eggs")
1436 self.handled.wait()
1437 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001438
1439
1440@unittest.skipUnless(threading, 'Threading required for this test.')
1441class SysLogHandlerTest(BaseTest):
1442
1443 """Test for SysLogHandler using UDP."""
1444
1445 def setUp(self):
1446 """Set up a UDP server to receive log messages, and a SysLogHandler
1447 pointing to that server's address and port."""
1448 BaseTest.setUp(self)
1449 addr = ('localhost', 0)
1450 self.server = server = TestUDPServer(addr, self.handle_datagram,
1451 0.01)
1452 server.start()
1453 server.ready.wait()
1454 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1455 server.port))
1456 self.log_output = ''
1457 self.root_logger.removeHandler(self.root_logger.handlers[0])
1458 self.root_logger.addHandler(self.sl_hdlr)
1459 self.handled = threading.Event()
1460
1461 def tearDown(self):
1462 """Shutdown the UDP server."""
1463 try:
1464 self.server.stop(2.0)
1465 self.root_logger.removeHandler(self.sl_hdlr)
1466 self.sl_hdlr.close()
1467 finally:
1468 BaseTest.tearDown(self)
1469
1470 def handle_datagram(self, request):
1471 self.log_output = request.packet
1472 self.handled.set()
1473
1474 def test_output(self):
1475 # The log message sent to the SysLogHandler is properly received.
1476 logger = logging.getLogger("slh")
1477 logger.error("sp\xe4m")
1478 self.handled.wait()
1479 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001480 self.handled.clear()
1481 self.sl_hdlr.append_nul = False
1482 logger.error("sp\xe4m")
1483 self.handled.wait()
1484 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001485
1486
1487@unittest.skipUnless(threading, 'Threading required for this test.')
1488class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001489 """Test for HTTPHandler."""
1490
Vinay Sajip9ba87612011-05-21 11:32:15 +01001491 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1492MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1493zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1494aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1495AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
14960tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
14975LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1498piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
14992z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1500i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1501PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1502E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1503rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1504/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1505-----END RSA PRIVATE KEY-----
1506-----BEGIN CERTIFICATE-----
1507MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1508BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1509EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1510xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1511l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1512vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1513BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1514S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1515BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15164f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15171c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1518IRbTpvlHWPjsSvHz0ZOH
1519-----END CERTIFICATE-----"""
1520
Vinay Sajip7367d082011-05-02 13:17:27 +01001521 def setUp(self):
1522 """Set up an HTTP server to receive log messages, and a HTTPHandler
1523 pointing to that server's address and port."""
1524 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001525 self.handled = threading.Event()
1526
Vinay Sajip7367d082011-05-02 13:17:27 +01001527 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001528 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001529 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001530 if self.command == 'POST':
1531 try:
1532 rlen = int(request.headers['Content-Length'])
1533 self.post_data = request.rfile.read(rlen)
1534 except:
1535 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001536 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001537 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001538 self.handled.set()
1539
1540 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001541 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001542 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001543 root_logger = self.root_logger
1544 root_logger.removeHandler(self.root_logger.handlers[0])
1545 for secure in (False, True):
1546 addr = ('localhost', 0)
1547 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001548 try:
1549 import ssl
1550 fd, fn = tempfile.mkstemp()
1551 os.close(fd)
1552 with open(fn, 'w') as f:
1553 f.write(self.PEMFILE)
1554 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1555 sslctx.load_cert_chain(fn)
1556 os.unlink(fn)
1557 except ImportError:
1558 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001559 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001560 sslctx = None
1561 self.server = server = TestHTTPServer(addr, self.handle_request,
1562 0.01, sslctx=sslctx)
1563 server.start()
1564 server.ready.wait()
1565 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001566 secure_client = secure and sslctx
1567 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1568 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001569 self.log_data = None
1570 root_logger.addHandler(self.h_hdlr)
1571
1572 for method in ('GET', 'POST'):
1573 self.h_hdlr.method = method
1574 self.handled.clear()
1575 msg = "sp\xe4m"
1576 logger.error(msg)
1577 self.handled.wait()
1578 self.assertEqual(self.log_data.path, '/frob')
1579 self.assertEqual(self.command, method)
1580 if method == 'GET':
1581 d = parse_qs(self.log_data.query)
1582 else:
1583 d = parse_qs(self.post_data.decode('utf-8'))
1584 self.assertEqual(d['name'], ['http'])
1585 self.assertEqual(d['funcName'], ['test_output'])
1586 self.assertEqual(d['msg'], [msg])
1587
1588 self.server.stop(2.0)
1589 self.root_logger.removeHandler(self.h_hdlr)
1590 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001591
Christian Heimes180510d2008-03-03 19:15:45 +00001592class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001593
Christian Heimes180510d2008-03-03 19:15:45 +00001594 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001595
Christian Heimes180510d2008-03-03 19:15:45 +00001596 def setUp(self):
1597 """Create a dict to remember potentially destroyed objects."""
1598 BaseTest.setUp(self)
1599 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001600
Christian Heimes180510d2008-03-03 19:15:45 +00001601 def _watch_for_survival(self, *args):
1602 """Watch the given objects for survival, by creating weakrefs to
1603 them."""
1604 for obj in args:
1605 key = id(obj), repr(obj)
1606 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001607
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001608 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001609 """Assert that all objects watched for survival have survived."""
1610 # Trigger cycle breaking.
1611 gc.collect()
1612 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001613 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001614 if ref() is None:
1615 dead.append(repr_)
1616 if dead:
1617 self.fail("%d objects should have survived "
1618 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001619
Christian Heimes180510d2008-03-03 19:15:45 +00001620 def test_persistent_loggers(self):
1621 # Logger objects are persistent and retain their configuration, even
1622 # if visible references are destroyed.
1623 self.root_logger.setLevel(logging.INFO)
1624 foo = logging.getLogger("foo")
1625 self._watch_for_survival(foo)
1626 foo.setLevel(logging.DEBUG)
1627 self.root_logger.debug(self.next_message())
1628 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001629 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001630 ('foo', 'DEBUG', '2'),
1631 ])
1632 del foo
1633 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001634 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001635 # foo has retained its settings.
1636 bar = logging.getLogger("foo")
1637 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001638 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001639 ('foo', 'DEBUG', '2'),
1640 ('foo', 'DEBUG', '3'),
1641 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001642
Benjamin Petersonf91df042009-02-13 02:50:59 +00001643
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001644class EncodingTest(BaseTest):
1645 def test_encoding_plain_file(self):
1646 # In Python 2.x, a plain file object is treated as having no encoding.
1647 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001648 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1649 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001650 # the non-ascii data we write to the log.
1651 data = "foo\x80"
1652 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001653 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001654 log.addHandler(handler)
1655 try:
1656 # write non-ascii data to the log.
1657 log.warning(data)
1658 finally:
1659 log.removeHandler(handler)
1660 handler.close()
1661 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001662 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001663 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001664 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001665 finally:
1666 f.close()
1667 finally:
1668 if os.path.isfile(fn):
1669 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001670
Benjamin Petersonf91df042009-02-13 02:50:59 +00001671 def test_encoding_cyrillic_unicode(self):
1672 log = logging.getLogger("test")
1673 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1674 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1675 #Ensure it's written in a Cyrillic encoding
1676 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001677 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001678 stream = io.BytesIO()
1679 writer = writer_class(stream, 'strict')
1680 handler = logging.StreamHandler(writer)
1681 log.addHandler(handler)
1682 try:
1683 log.warning(message)
1684 finally:
1685 log.removeHandler(handler)
1686 handler.close()
1687 # check we wrote exactly those bytes, ignoring trailing \n etc
1688 s = stream.getvalue()
1689 #Compare against what the data should be when encoded in CP-1251
1690 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1691
1692
Georg Brandlf9734072008-12-07 15:30:06 +00001693class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001694
Georg Brandlf9734072008-12-07 15:30:06 +00001695 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001696 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001697 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001698 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001699 warnings.filterwarnings("always", category=UserWarning)
1700 stream = io.StringIO()
1701 h = logging.StreamHandler(stream)
1702 logger = logging.getLogger("py.warnings")
1703 logger.addHandler(h)
1704 warnings.warn("I'm warning you...")
1705 logger.removeHandler(h)
1706 s = stream.getvalue()
1707 h.close()
1708 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001709
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001710 #See if an explicit file uses the original implementation
1711 a_file = io.StringIO()
1712 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1713 a_file, "Dummy line")
1714 s = a_file.getvalue()
1715 a_file.close()
1716 self.assertEqual(s,
1717 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1718
1719 def test_warnings_no_handlers(self):
1720 with warnings.catch_warnings():
1721 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001722 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001723
1724 # confirm our assumption: no loggers are set
1725 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001726 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001727
1728 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001729 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001730 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001731
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001732
1733def formatFunc(format, datefmt=None):
1734 return logging.Formatter(format, datefmt)
1735
1736def handlerFunc():
1737 return logging.StreamHandler()
1738
1739class CustomHandler(logging.StreamHandler):
1740 pass
1741
1742class ConfigDictTest(BaseTest):
1743
1744 """Reading logging config from a dictionary."""
1745
1746 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1747
1748 # config0 is a standard configuration.
1749 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001750 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001751 'formatters': {
1752 'form1' : {
1753 'format' : '%(levelname)s ++ %(message)s',
1754 },
1755 },
1756 'handlers' : {
1757 'hand1' : {
1758 'class' : 'logging.StreamHandler',
1759 'formatter' : 'form1',
1760 'level' : 'NOTSET',
1761 'stream' : 'ext://sys.stdout',
1762 },
1763 },
1764 'root' : {
1765 'level' : 'WARNING',
1766 'handlers' : ['hand1'],
1767 },
1768 }
1769
1770 # config1 adds a little to the standard configuration.
1771 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001772 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001773 'formatters': {
1774 'form1' : {
1775 'format' : '%(levelname)s ++ %(message)s',
1776 },
1777 },
1778 'handlers' : {
1779 'hand1' : {
1780 'class' : 'logging.StreamHandler',
1781 'formatter' : 'form1',
1782 'level' : 'NOTSET',
1783 'stream' : 'ext://sys.stdout',
1784 },
1785 },
1786 'loggers' : {
1787 'compiler.parser' : {
1788 'level' : 'DEBUG',
1789 'handlers' : ['hand1'],
1790 },
1791 },
1792 'root' : {
1793 'level' : 'WARNING',
1794 },
1795 }
1796
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001797 # config1a moves the handler to the root. Used with config8a
1798 config1a = {
1799 'version': 1,
1800 'formatters': {
1801 'form1' : {
1802 'format' : '%(levelname)s ++ %(message)s',
1803 },
1804 },
1805 'handlers' : {
1806 'hand1' : {
1807 'class' : 'logging.StreamHandler',
1808 'formatter' : 'form1',
1809 'level' : 'NOTSET',
1810 'stream' : 'ext://sys.stdout',
1811 },
1812 },
1813 'loggers' : {
1814 'compiler.parser' : {
1815 'level' : 'DEBUG',
1816 },
1817 },
1818 'root' : {
1819 'level' : 'WARNING',
1820 'handlers' : ['hand1'],
1821 },
1822 }
1823
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001824 # config2 has a subtle configuration error that should be reported
1825 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001826 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001827 'formatters': {
1828 'form1' : {
1829 'format' : '%(levelname)s ++ %(message)s',
1830 },
1831 },
1832 'handlers' : {
1833 'hand1' : {
1834 'class' : 'logging.StreamHandler',
1835 'formatter' : 'form1',
1836 'level' : 'NOTSET',
1837 'stream' : 'ext://sys.stdbout',
1838 },
1839 },
1840 'loggers' : {
1841 'compiler.parser' : {
1842 'level' : 'DEBUG',
1843 'handlers' : ['hand1'],
1844 },
1845 },
1846 'root' : {
1847 'level' : 'WARNING',
1848 },
1849 }
1850
1851 #As config1 but with a misspelt level on a handler
1852 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001853 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001854 'formatters': {
1855 'form1' : {
1856 'format' : '%(levelname)s ++ %(message)s',
1857 },
1858 },
1859 'handlers' : {
1860 'hand1' : {
1861 'class' : 'logging.StreamHandler',
1862 'formatter' : 'form1',
1863 'level' : 'NTOSET',
1864 'stream' : 'ext://sys.stdout',
1865 },
1866 },
1867 'loggers' : {
1868 'compiler.parser' : {
1869 'level' : 'DEBUG',
1870 'handlers' : ['hand1'],
1871 },
1872 },
1873 'root' : {
1874 'level' : 'WARNING',
1875 },
1876 }
1877
1878
1879 #As config1 but with a misspelt level on a logger
1880 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001881 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001882 'formatters': {
1883 'form1' : {
1884 'format' : '%(levelname)s ++ %(message)s',
1885 },
1886 },
1887 'handlers' : {
1888 'hand1' : {
1889 'class' : 'logging.StreamHandler',
1890 'formatter' : 'form1',
1891 'level' : 'NOTSET',
1892 'stream' : 'ext://sys.stdout',
1893 },
1894 },
1895 'loggers' : {
1896 'compiler.parser' : {
1897 'level' : 'DEBUG',
1898 'handlers' : ['hand1'],
1899 },
1900 },
1901 'root' : {
1902 'level' : 'WRANING',
1903 },
1904 }
1905
1906 # config3 has a less subtle configuration error
1907 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001908 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001909 'formatters': {
1910 'form1' : {
1911 'format' : '%(levelname)s ++ %(message)s',
1912 },
1913 },
1914 'handlers' : {
1915 'hand1' : {
1916 'class' : 'logging.StreamHandler',
1917 'formatter' : 'misspelled_name',
1918 'level' : 'NOTSET',
1919 'stream' : 'ext://sys.stdout',
1920 },
1921 },
1922 'loggers' : {
1923 'compiler.parser' : {
1924 'level' : 'DEBUG',
1925 'handlers' : ['hand1'],
1926 },
1927 },
1928 'root' : {
1929 'level' : 'WARNING',
1930 },
1931 }
1932
1933 # config4 specifies a custom formatter class to be loaded
1934 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001935 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001936 'formatters': {
1937 'form1' : {
1938 '()' : __name__ + '.ExceptionFormatter',
1939 'format' : '%(levelname)s:%(name)s:%(message)s',
1940 },
1941 },
1942 'handlers' : {
1943 'hand1' : {
1944 'class' : 'logging.StreamHandler',
1945 'formatter' : 'form1',
1946 'level' : 'NOTSET',
1947 'stream' : 'ext://sys.stdout',
1948 },
1949 },
1950 'root' : {
1951 'level' : 'NOTSET',
1952 'handlers' : ['hand1'],
1953 },
1954 }
1955
1956 # As config4 but using an actual callable rather than a string
1957 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001958 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001959 'formatters': {
1960 'form1' : {
1961 '()' : ExceptionFormatter,
1962 'format' : '%(levelname)s:%(name)s:%(message)s',
1963 },
1964 'form2' : {
1965 '()' : __name__ + '.formatFunc',
1966 'format' : '%(levelname)s:%(name)s:%(message)s',
1967 },
1968 'form3' : {
1969 '()' : formatFunc,
1970 'format' : '%(levelname)s:%(name)s:%(message)s',
1971 },
1972 },
1973 'handlers' : {
1974 'hand1' : {
1975 'class' : 'logging.StreamHandler',
1976 'formatter' : 'form1',
1977 'level' : 'NOTSET',
1978 'stream' : 'ext://sys.stdout',
1979 },
1980 'hand2' : {
1981 '()' : handlerFunc,
1982 },
1983 },
1984 'root' : {
1985 'level' : 'NOTSET',
1986 'handlers' : ['hand1'],
1987 },
1988 }
1989
1990 # config5 specifies a custom handler class to be loaded
1991 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001992 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001993 'formatters': {
1994 'form1' : {
1995 'format' : '%(levelname)s ++ %(message)s',
1996 },
1997 },
1998 'handlers' : {
1999 'hand1' : {
2000 'class' : __name__ + '.CustomHandler',
2001 'formatter' : 'form1',
2002 'level' : 'NOTSET',
2003 'stream' : 'ext://sys.stdout',
2004 },
2005 },
2006 'loggers' : {
2007 'compiler.parser' : {
2008 'level' : 'DEBUG',
2009 'handlers' : ['hand1'],
2010 },
2011 },
2012 'root' : {
2013 'level' : 'WARNING',
2014 },
2015 }
2016
2017 # config6 specifies a custom handler class to be loaded
2018 # but has bad arguments
2019 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002020 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002021 'formatters': {
2022 'form1' : {
2023 'format' : '%(levelname)s ++ %(message)s',
2024 },
2025 },
2026 'handlers' : {
2027 'hand1' : {
2028 'class' : __name__ + '.CustomHandler',
2029 'formatter' : 'form1',
2030 'level' : 'NOTSET',
2031 'stream' : 'ext://sys.stdout',
2032 '9' : 'invalid parameter name',
2033 },
2034 },
2035 'loggers' : {
2036 'compiler.parser' : {
2037 'level' : 'DEBUG',
2038 'handlers' : ['hand1'],
2039 },
2040 },
2041 'root' : {
2042 'level' : 'WARNING',
2043 },
2044 }
2045
2046 #config 7 does not define compiler.parser but defines compiler.lexer
2047 #so compiler.parser should be disabled after applying it
2048 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002049 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002050 'formatters': {
2051 'form1' : {
2052 'format' : '%(levelname)s ++ %(message)s',
2053 },
2054 },
2055 'handlers' : {
2056 'hand1' : {
2057 'class' : 'logging.StreamHandler',
2058 'formatter' : 'form1',
2059 'level' : 'NOTSET',
2060 'stream' : 'ext://sys.stdout',
2061 },
2062 },
2063 'loggers' : {
2064 'compiler.lexer' : {
2065 'level' : 'DEBUG',
2066 'handlers' : ['hand1'],
2067 },
2068 },
2069 'root' : {
2070 'level' : 'WARNING',
2071 },
2072 }
2073
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002074 # config8 defines both compiler and compiler.lexer
2075 # so compiler.parser should not be disabled (since
2076 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002077 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002078 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002079 'disable_existing_loggers' : False,
2080 'formatters': {
2081 'form1' : {
2082 'format' : '%(levelname)s ++ %(message)s',
2083 },
2084 },
2085 'handlers' : {
2086 'hand1' : {
2087 'class' : 'logging.StreamHandler',
2088 'formatter' : 'form1',
2089 'level' : 'NOTSET',
2090 'stream' : 'ext://sys.stdout',
2091 },
2092 },
2093 'loggers' : {
2094 'compiler' : {
2095 'level' : 'DEBUG',
2096 'handlers' : ['hand1'],
2097 },
2098 'compiler.lexer' : {
2099 },
2100 },
2101 'root' : {
2102 'level' : 'WARNING',
2103 },
2104 }
2105
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002106 # config8a disables existing loggers
2107 config8a = {
2108 'version': 1,
2109 'disable_existing_loggers' : True,
2110 'formatters': {
2111 'form1' : {
2112 'format' : '%(levelname)s ++ %(message)s',
2113 },
2114 },
2115 'handlers' : {
2116 'hand1' : {
2117 'class' : 'logging.StreamHandler',
2118 'formatter' : 'form1',
2119 'level' : 'NOTSET',
2120 'stream' : 'ext://sys.stdout',
2121 },
2122 },
2123 'loggers' : {
2124 'compiler' : {
2125 'level' : 'DEBUG',
2126 'handlers' : ['hand1'],
2127 },
2128 'compiler.lexer' : {
2129 },
2130 },
2131 'root' : {
2132 'level' : 'WARNING',
2133 },
2134 }
2135
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002136 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002137 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002138 'formatters': {
2139 'form1' : {
2140 'format' : '%(levelname)s ++ %(message)s',
2141 },
2142 },
2143 'handlers' : {
2144 'hand1' : {
2145 'class' : 'logging.StreamHandler',
2146 'formatter' : 'form1',
2147 'level' : 'WARNING',
2148 'stream' : 'ext://sys.stdout',
2149 },
2150 },
2151 'loggers' : {
2152 'compiler.parser' : {
2153 'level' : 'WARNING',
2154 'handlers' : ['hand1'],
2155 },
2156 },
2157 'root' : {
2158 'level' : 'NOTSET',
2159 },
2160 }
2161
2162 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002163 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002164 'incremental' : True,
2165 'handlers' : {
2166 'hand1' : {
2167 'level' : 'WARNING',
2168 },
2169 },
2170 'loggers' : {
2171 'compiler.parser' : {
2172 'level' : 'INFO',
2173 },
2174 },
2175 }
2176
2177 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002178 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002179 'incremental' : True,
2180 'handlers' : {
2181 'hand1' : {
2182 'level' : 'INFO',
2183 },
2184 },
2185 'loggers' : {
2186 'compiler.parser' : {
2187 'level' : 'INFO',
2188 },
2189 },
2190 }
2191
2192 #As config1 but with a filter added
2193 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002194 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002195 'formatters': {
2196 'form1' : {
2197 'format' : '%(levelname)s ++ %(message)s',
2198 },
2199 },
2200 'filters' : {
2201 'filt1' : {
2202 'name' : 'compiler.parser',
2203 },
2204 },
2205 'handlers' : {
2206 'hand1' : {
2207 'class' : 'logging.StreamHandler',
2208 'formatter' : 'form1',
2209 'level' : 'NOTSET',
2210 'stream' : 'ext://sys.stdout',
2211 'filters' : ['filt1'],
2212 },
2213 },
2214 'loggers' : {
2215 'compiler.parser' : {
2216 'level' : 'DEBUG',
2217 'filters' : ['filt1'],
2218 },
2219 },
2220 'root' : {
2221 'level' : 'WARNING',
2222 'handlers' : ['hand1'],
2223 },
2224 }
2225
2226 #As config1 but using cfg:// references
2227 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002228 'version': 1,
2229 'true_formatters': {
2230 'form1' : {
2231 'format' : '%(levelname)s ++ %(message)s',
2232 },
2233 },
2234 'handler_configs': {
2235 'hand1' : {
2236 'class' : 'logging.StreamHandler',
2237 'formatter' : 'form1',
2238 'level' : 'NOTSET',
2239 'stream' : 'ext://sys.stdout',
2240 },
2241 },
2242 'formatters' : 'cfg://true_formatters',
2243 'handlers' : {
2244 'hand1' : 'cfg://handler_configs[hand1]',
2245 },
2246 'loggers' : {
2247 'compiler.parser' : {
2248 'level' : 'DEBUG',
2249 'handlers' : ['hand1'],
2250 },
2251 },
2252 'root' : {
2253 'level' : 'WARNING',
2254 },
2255 }
2256
2257 #As config11 but missing the version key
2258 config12 = {
2259 'true_formatters': {
2260 'form1' : {
2261 'format' : '%(levelname)s ++ %(message)s',
2262 },
2263 },
2264 'handler_configs': {
2265 'hand1' : {
2266 'class' : 'logging.StreamHandler',
2267 'formatter' : 'form1',
2268 'level' : 'NOTSET',
2269 'stream' : 'ext://sys.stdout',
2270 },
2271 },
2272 'formatters' : 'cfg://true_formatters',
2273 'handlers' : {
2274 'hand1' : 'cfg://handler_configs[hand1]',
2275 },
2276 'loggers' : {
2277 'compiler.parser' : {
2278 'level' : 'DEBUG',
2279 'handlers' : ['hand1'],
2280 },
2281 },
2282 'root' : {
2283 'level' : 'WARNING',
2284 },
2285 }
2286
2287 #As config11 but using an unsupported version
2288 config13 = {
2289 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002290 'true_formatters': {
2291 'form1' : {
2292 'format' : '%(levelname)s ++ %(message)s',
2293 },
2294 },
2295 'handler_configs': {
2296 'hand1' : {
2297 'class' : 'logging.StreamHandler',
2298 'formatter' : 'form1',
2299 'level' : 'NOTSET',
2300 'stream' : 'ext://sys.stdout',
2301 },
2302 },
2303 'formatters' : 'cfg://true_formatters',
2304 'handlers' : {
2305 'hand1' : 'cfg://handler_configs[hand1]',
2306 },
2307 'loggers' : {
2308 'compiler.parser' : {
2309 'level' : 'DEBUG',
2310 'handlers' : ['hand1'],
2311 },
2312 },
2313 'root' : {
2314 'level' : 'WARNING',
2315 },
2316 }
2317
2318 def apply_config(self, conf):
2319 logging.config.dictConfig(conf)
2320
2321 def test_config0_ok(self):
2322 # A simple config which overrides the default settings.
2323 with captured_stdout() as output:
2324 self.apply_config(self.config0)
2325 logger = logging.getLogger()
2326 # Won't output anything
2327 logger.info(self.next_message())
2328 # Outputs a message
2329 logger.error(self.next_message())
2330 self.assert_log_lines([
2331 ('ERROR', '2'),
2332 ], stream=output)
2333 # Original logger output is empty.
2334 self.assert_log_lines([])
2335
2336 def test_config1_ok(self, config=config1):
2337 # A config defining a sub-parser as well.
2338 with captured_stdout() as output:
2339 self.apply_config(config)
2340 logger = logging.getLogger("compiler.parser")
2341 # Both will output a message
2342 logger.info(self.next_message())
2343 logger.error(self.next_message())
2344 self.assert_log_lines([
2345 ('INFO', '1'),
2346 ('ERROR', '2'),
2347 ], stream=output)
2348 # Original logger output is empty.
2349 self.assert_log_lines([])
2350
2351 def test_config2_failure(self):
2352 # A simple config which overrides the default settings.
2353 self.assertRaises(Exception, self.apply_config, self.config2)
2354
2355 def test_config2a_failure(self):
2356 # A simple config which overrides the default settings.
2357 self.assertRaises(Exception, self.apply_config, self.config2a)
2358
2359 def test_config2b_failure(self):
2360 # A simple config which overrides the default settings.
2361 self.assertRaises(Exception, self.apply_config, self.config2b)
2362
2363 def test_config3_failure(self):
2364 # A simple config which overrides the default settings.
2365 self.assertRaises(Exception, self.apply_config, self.config3)
2366
2367 def test_config4_ok(self):
2368 # A config specifying a custom formatter class.
2369 with captured_stdout() as output:
2370 self.apply_config(self.config4)
2371 #logger = logging.getLogger()
2372 try:
2373 raise RuntimeError()
2374 except RuntimeError:
2375 logging.exception("just testing")
2376 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002377 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002378 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2379 # Original logger output is empty
2380 self.assert_log_lines([])
2381
2382 def test_config4a_ok(self):
2383 # A config specifying a custom formatter class.
2384 with captured_stdout() as output:
2385 self.apply_config(self.config4a)
2386 #logger = logging.getLogger()
2387 try:
2388 raise RuntimeError()
2389 except RuntimeError:
2390 logging.exception("just testing")
2391 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002392 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002393 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2394 # Original logger output is empty
2395 self.assert_log_lines([])
2396
2397 def test_config5_ok(self):
2398 self.test_config1_ok(config=self.config5)
2399
2400 def test_config6_failure(self):
2401 self.assertRaises(Exception, self.apply_config, self.config6)
2402
2403 def test_config7_ok(self):
2404 with captured_stdout() as output:
2405 self.apply_config(self.config1)
2406 logger = logging.getLogger("compiler.parser")
2407 # Both will output a message
2408 logger.info(self.next_message())
2409 logger.error(self.next_message())
2410 self.assert_log_lines([
2411 ('INFO', '1'),
2412 ('ERROR', '2'),
2413 ], stream=output)
2414 # Original logger output is empty.
2415 self.assert_log_lines([])
2416 with captured_stdout() as output:
2417 self.apply_config(self.config7)
2418 logger = logging.getLogger("compiler.parser")
2419 self.assertTrue(logger.disabled)
2420 logger = logging.getLogger("compiler.lexer")
2421 # Both will output a message
2422 logger.info(self.next_message())
2423 logger.error(self.next_message())
2424 self.assert_log_lines([
2425 ('INFO', '3'),
2426 ('ERROR', '4'),
2427 ], stream=output)
2428 # Original logger output is empty.
2429 self.assert_log_lines([])
2430
2431 #Same as test_config_7_ok but don't disable old loggers.
2432 def test_config_8_ok(self):
2433 with captured_stdout() as output:
2434 self.apply_config(self.config1)
2435 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002436 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002437 logger.info(self.next_message())
2438 logger.error(self.next_message())
2439 self.assert_log_lines([
2440 ('INFO', '1'),
2441 ('ERROR', '2'),
2442 ], stream=output)
2443 # Original logger output is empty.
2444 self.assert_log_lines([])
2445 with captured_stdout() as output:
2446 self.apply_config(self.config8)
2447 logger = logging.getLogger("compiler.parser")
2448 self.assertFalse(logger.disabled)
2449 # Both will output a message
2450 logger.info(self.next_message())
2451 logger.error(self.next_message())
2452 logger = logging.getLogger("compiler.lexer")
2453 # Both will output a message
2454 logger.info(self.next_message())
2455 logger.error(self.next_message())
2456 self.assert_log_lines([
2457 ('INFO', '3'),
2458 ('ERROR', '4'),
2459 ('INFO', '5'),
2460 ('ERROR', '6'),
2461 ], stream=output)
2462 # Original logger output is empty.
2463 self.assert_log_lines([])
2464
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002465 def test_config_8a_ok(self):
2466 with captured_stdout() as output:
2467 self.apply_config(self.config1a)
2468 logger = logging.getLogger("compiler.parser")
2469 # See issue #11424. compiler-hyphenated sorts
2470 # between compiler and compiler.xyz and this
2471 # was preventing compiler.xyz from being included
2472 # in the child loggers of compiler because of an
2473 # overzealous loop termination condition.
2474 hyphenated = logging.getLogger('compiler-hyphenated')
2475 # All will output a message
2476 logger.info(self.next_message())
2477 logger.error(self.next_message())
2478 hyphenated.critical(self.next_message())
2479 self.assert_log_lines([
2480 ('INFO', '1'),
2481 ('ERROR', '2'),
2482 ('CRITICAL', '3'),
2483 ], stream=output)
2484 # Original logger output is empty.
2485 self.assert_log_lines([])
2486 with captured_stdout() as output:
2487 self.apply_config(self.config8a)
2488 logger = logging.getLogger("compiler.parser")
2489 self.assertFalse(logger.disabled)
2490 # Both will output a message
2491 logger.info(self.next_message())
2492 logger.error(self.next_message())
2493 logger = logging.getLogger("compiler.lexer")
2494 # Both will output a message
2495 logger.info(self.next_message())
2496 logger.error(self.next_message())
2497 # Will not appear
2498 hyphenated.critical(self.next_message())
2499 self.assert_log_lines([
2500 ('INFO', '4'),
2501 ('ERROR', '5'),
2502 ('INFO', '6'),
2503 ('ERROR', '7'),
2504 ], stream=output)
2505 # Original logger output is empty.
2506 self.assert_log_lines([])
2507
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002508 def test_config_9_ok(self):
2509 with captured_stdout() as output:
2510 self.apply_config(self.config9)
2511 logger = logging.getLogger("compiler.parser")
2512 #Nothing will be output since both handler and logger are set to WARNING
2513 logger.info(self.next_message())
2514 self.assert_log_lines([], stream=output)
2515 self.apply_config(self.config9a)
2516 #Nothing will be output since both handler is still set to WARNING
2517 logger.info(self.next_message())
2518 self.assert_log_lines([], stream=output)
2519 self.apply_config(self.config9b)
2520 #Message should now be output
2521 logger.info(self.next_message())
2522 self.assert_log_lines([
2523 ('INFO', '3'),
2524 ], stream=output)
2525
2526 def test_config_10_ok(self):
2527 with captured_stdout() as output:
2528 self.apply_config(self.config10)
2529 logger = logging.getLogger("compiler.parser")
2530 logger.warning(self.next_message())
2531 logger = logging.getLogger('compiler')
2532 #Not output, because filtered
2533 logger.warning(self.next_message())
2534 logger = logging.getLogger('compiler.lexer')
2535 #Not output, because filtered
2536 logger.warning(self.next_message())
2537 logger = logging.getLogger("compiler.parser.codegen")
2538 #Output, as not filtered
2539 logger.error(self.next_message())
2540 self.assert_log_lines([
2541 ('WARNING', '1'),
2542 ('ERROR', '4'),
2543 ], stream=output)
2544
2545 def test_config11_ok(self):
2546 self.test_config1_ok(self.config11)
2547
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002548 def test_config12_failure(self):
2549 self.assertRaises(Exception, self.apply_config, self.config12)
2550
2551 def test_config13_failure(self):
2552 self.assertRaises(Exception, self.apply_config, self.config13)
2553
Victor Stinner45df8202010-04-28 22:31:17 +00002554 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002555 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002556 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002557 # Ask for a randomly assigned port (by using port 0)
2558 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002559 t.start()
2560 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002561 # Now get the port allocated
2562 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002563 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002564 try:
2565 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2566 sock.settimeout(2.0)
2567 sock.connect(('localhost', port))
2568
2569 slen = struct.pack('>L', len(text))
2570 s = slen + text
2571 sentsofar = 0
2572 left = len(s)
2573 while left > 0:
2574 sent = sock.send(s[sentsofar:])
2575 sentsofar += sent
2576 left -= sent
2577 sock.close()
2578 finally:
2579 t.ready.wait(2.0)
2580 logging.config.stopListening()
2581 t.join(2.0)
2582
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002583 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002584 def test_listen_config_10_ok(self):
2585 with captured_stdout() as output:
2586 self.setup_via_listener(json.dumps(self.config10))
2587 logger = logging.getLogger("compiler.parser")
2588 logger.warning(self.next_message())
2589 logger = logging.getLogger('compiler')
2590 #Not output, because filtered
2591 logger.warning(self.next_message())
2592 logger = logging.getLogger('compiler.lexer')
2593 #Not output, because filtered
2594 logger.warning(self.next_message())
2595 logger = logging.getLogger("compiler.parser.codegen")
2596 #Output, as not filtered
2597 logger.error(self.next_message())
2598 self.assert_log_lines([
2599 ('WARNING', '1'),
2600 ('ERROR', '4'),
2601 ], stream=output)
2602
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002603 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002604 def test_listen_config_1_ok(self):
2605 with captured_stdout() as output:
2606 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2607 logger = logging.getLogger("compiler.parser")
2608 # Both will output a message
2609 logger.info(self.next_message())
2610 logger.error(self.next_message())
2611 self.assert_log_lines([
2612 ('INFO', '1'),
2613 ('ERROR', '2'),
2614 ], stream=output)
2615 # Original logger output is empty.
2616 self.assert_log_lines([])
2617
Vinay Sajip373baef2011-04-26 20:05:24 +01002618 def test_baseconfig(self):
2619 d = {
2620 'atuple': (1, 2, 3),
2621 'alist': ['a', 'b', 'c'],
2622 'adict': {'d': 'e', 'f': 3 },
2623 'nest1': ('g', ('h', 'i'), 'j'),
2624 'nest2': ['k', ['l', 'm'], 'n'],
2625 'nest3': ['o', 'cfg://alist', 'p'],
2626 }
2627 bc = logging.config.BaseConfigurator(d)
2628 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2629 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2630 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2631 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2632 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2633 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2634 v = bc.convert('cfg://nest3')
2635 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2636 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2637 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2638 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002639
2640class ManagerTest(BaseTest):
2641 def test_manager_loggerclass(self):
2642 logged = []
2643
2644 class MyLogger(logging.Logger):
2645 def _log(self, level, msg, args, exc_info=None, extra=None):
2646 logged.append(msg)
2647
2648 man = logging.Manager(None)
2649 self.assertRaises(TypeError, man.setLoggerClass, int)
2650 man.setLoggerClass(MyLogger)
2651 logger = man.getLogger('test')
2652 logger.warning('should appear in logged')
2653 logging.warning('should not appear in logged')
2654
2655 self.assertEqual(logged, ['should appear in logged'])
2656
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002657 def test_set_log_record_factory(self):
2658 man = logging.Manager(None)
2659 expected = object()
2660 man.setLogRecordFactory(expected)
2661 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002662
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002663class ChildLoggerTest(BaseTest):
2664 def test_child_loggers(self):
2665 r = logging.getLogger()
2666 l1 = logging.getLogger('abc')
2667 l2 = logging.getLogger('def.ghi')
2668 c1 = r.getChild('xyz')
2669 c2 = r.getChild('uvw.xyz')
2670 self.assertTrue(c1 is logging.getLogger('xyz'))
2671 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2672 c1 = l1.getChild('def')
2673 c2 = c1.getChild('ghi')
2674 c3 = l1.getChild('def.ghi')
2675 self.assertTrue(c1 is logging.getLogger('abc.def'))
2676 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2677 self.assertTrue(c2 is c3)
2678
2679
Vinay Sajip6fac8172010-10-19 20:44:14 +00002680class DerivedLogRecord(logging.LogRecord):
2681 pass
2682
Vinay Sajip61561522010-12-03 11:50:38 +00002683class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002684
2685 def setUp(self):
2686 class CheckingFilter(logging.Filter):
2687 def __init__(self, cls):
2688 self.cls = cls
2689
2690 def filter(self, record):
2691 t = type(record)
2692 if t is not self.cls:
2693 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2694 self.cls)
2695 raise TypeError(msg)
2696 return True
2697
2698 BaseTest.setUp(self)
2699 self.filter = CheckingFilter(DerivedLogRecord)
2700 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002701 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002702
2703 def tearDown(self):
2704 self.root_logger.removeFilter(self.filter)
2705 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002706 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002707
2708 def test_logrecord_class(self):
2709 self.assertRaises(TypeError, self.root_logger.warning,
2710 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002711 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002712 self.root_logger.error(self.next_message())
2713 self.assert_log_lines([
2714 ('root', 'ERROR', '2'),
2715 ])
2716
2717
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002718class QueueHandlerTest(BaseTest):
2719 # Do not bother with a logger name group.
2720 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2721
2722 def setUp(self):
2723 BaseTest.setUp(self)
2724 self.queue = queue.Queue(-1)
2725 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2726 self.que_logger = logging.getLogger('que')
2727 self.que_logger.propagate = False
2728 self.que_logger.setLevel(logging.WARNING)
2729 self.que_logger.addHandler(self.que_hdlr)
2730
2731 def tearDown(self):
2732 self.que_hdlr.close()
2733 BaseTest.tearDown(self)
2734
2735 def test_queue_handler(self):
2736 self.que_logger.debug(self.next_message())
2737 self.assertRaises(queue.Empty, self.queue.get_nowait)
2738 self.que_logger.info(self.next_message())
2739 self.assertRaises(queue.Empty, self.queue.get_nowait)
2740 msg = self.next_message()
2741 self.que_logger.warning(msg)
2742 data = self.queue.get_nowait()
2743 self.assertTrue(isinstance(data, logging.LogRecord))
2744 self.assertEqual(data.name, self.que_logger.name)
2745 self.assertEqual((data.msg, data.args), (msg, None))
2746
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002747 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2748 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002749 def test_queue_listener(self):
2750 handler = TestHandler(Matcher())
2751 listener = logging.handlers.QueueListener(self.queue, handler)
2752 listener.start()
2753 try:
2754 self.que_logger.warning(self.next_message())
2755 self.que_logger.error(self.next_message())
2756 self.que_logger.critical(self.next_message())
2757 finally:
2758 listener.stop()
2759 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2760 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2761 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2762
Vinay Sajip37eb3382011-04-26 20:26:41 +01002763ZERO = datetime.timedelta(0)
2764
2765class UTC(datetime.tzinfo):
2766 def utcoffset(self, dt):
2767 return ZERO
2768
2769 dst = utcoffset
2770
2771 def tzname(self, dt):
2772 return 'UTC'
2773
2774utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002775
Vinay Sajipa39c5712010-10-25 13:57:39 +00002776class FormatterTest(unittest.TestCase):
2777 def setUp(self):
2778 self.common = {
2779 'name': 'formatter.test',
2780 'level': logging.DEBUG,
2781 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2782 'lineno': 42,
2783 'exc_info': None,
2784 'func': None,
2785 'msg': 'Message with %d %s',
2786 'args': (2, 'placeholders'),
2787 }
2788 self.variants = {
2789 }
2790
2791 def get_record(self, name=None):
2792 result = dict(self.common)
2793 if name is not None:
2794 result.update(self.variants[name])
2795 return logging.makeLogRecord(result)
2796
2797 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002798 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002799 r = self.get_record()
2800 f = logging.Formatter('${%(message)s}')
2801 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2802 f = logging.Formatter('%(random)s')
2803 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002804 self.assertFalse(f.usesTime())
2805 f = logging.Formatter('%(asctime)s')
2806 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002807 f = logging.Formatter('%(asctime)-15s')
2808 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002809 f = logging.Formatter('asctime')
2810 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002811
2812 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002813 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002814 r = self.get_record()
2815 f = logging.Formatter('$%{message}%$', style='{')
2816 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2817 f = logging.Formatter('{random}', style='{')
2818 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002819 self.assertFalse(f.usesTime())
2820 f = logging.Formatter('{asctime}', style='{')
2821 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002822 f = logging.Formatter('{asctime!s:15}', style='{')
2823 self.assertTrue(f.usesTime())
2824 f = logging.Formatter('{asctime:15}', style='{')
2825 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002826 f = logging.Formatter('asctime', style='{')
2827 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002828
2829 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002830 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002831 r = self.get_record()
2832 f = logging.Formatter('$message', style='$')
2833 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2834 f = logging.Formatter('$$%${message}%$$', style='$')
2835 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2836 f = logging.Formatter('${random}', style='$')
2837 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002838 self.assertFalse(f.usesTime())
2839 f = logging.Formatter('${asctime}', style='$')
2840 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002841 f = logging.Formatter('${asctime', style='$')
2842 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002843 f = logging.Formatter('$asctime', style='$')
2844 self.assertTrue(f.usesTime())
2845 f = logging.Formatter('asctime', style='$')
2846 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002847
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002848 def test_invalid_style(self):
2849 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2850
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002851 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002852 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002853 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002854 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002855 r.msecs = 123
2856 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002857 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002858 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2859 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002860 f.format(r)
2861 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2862
2863class TestBufferingFormatter(logging.BufferingFormatter):
2864 def formatHeader(self, records):
2865 return '[(%d)' % len(records)
2866
2867 def formatFooter(self, records):
2868 return '(%d)]' % len(records)
2869
2870class BufferingFormatterTest(unittest.TestCase):
2871 def setUp(self):
2872 self.records = [
2873 logging.makeLogRecord({'msg': 'one'}),
2874 logging.makeLogRecord({'msg': 'two'}),
2875 ]
2876
2877 def test_default(self):
2878 f = logging.BufferingFormatter()
2879 self.assertEqual('', f.format([]))
2880 self.assertEqual('onetwo', f.format(self.records))
2881
2882 def test_custom(self):
2883 f = TestBufferingFormatter()
2884 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2885 lf = logging.Formatter('<%(message)s>')
2886 f = TestBufferingFormatter(lf)
2887 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002888
2889class ExceptionTest(BaseTest):
2890 def test_formatting(self):
2891 r = self.root_logger
2892 h = RecordingHandler()
2893 r.addHandler(h)
2894 try:
2895 raise RuntimeError('deliberate mistake')
2896 except:
2897 logging.exception('failed', stack_info=True)
2898 r.removeHandler(h)
2899 h.close()
2900 r = h.records[0]
2901 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2902 'call last):\n'))
2903 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2904 'deliberate mistake'))
2905 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2906 'call last):\n'))
2907 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2908 'stack_info=True)'))
2909
2910
Vinay Sajip5a27d402010-12-10 11:42:57 +00002911class LastResortTest(BaseTest):
2912 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002913 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002914 root = self.root_logger
2915 root.removeHandler(self.root_hdlr)
2916 old_stderr = sys.stderr
2917 old_lastresort = logging.lastResort
2918 old_raise_exceptions = logging.raiseExceptions
2919 try:
2920 sys.stderr = sio = io.StringIO()
2921 root.warning('This is your final chance!')
2922 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2923 #No handlers and no last resort, so 'No handlers' message
2924 logging.lastResort = None
2925 sys.stderr = sio = io.StringIO()
2926 root.warning('This is your final chance!')
2927 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2928 # 'No handlers' message only printed once
2929 sys.stderr = sio = io.StringIO()
2930 root.warning('This is your final chance!')
2931 self.assertEqual(sio.getvalue(), '')
2932 root.manager.emittedNoHandlerWarning = False
2933 #If raiseExceptions is False, no message is printed
2934 logging.raiseExceptions = False
2935 sys.stderr = sio = io.StringIO()
2936 root.warning('This is your final chance!')
2937 self.assertEqual(sio.getvalue(), '')
2938 finally:
2939 sys.stderr = old_stderr
2940 root.addHandler(self.root_hdlr)
2941 logging.lastResort = old_lastresort
2942 logging.raiseExceptions = old_raise_exceptions
2943
2944
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002945class FakeHandler:
2946
2947 def __init__(self, identifier, called):
2948 for method in ('acquire', 'flush', 'close', 'release'):
2949 setattr(self, method, self.record_call(identifier, method, called))
2950
2951 def record_call(self, identifier, method_name, called):
2952 def inner():
2953 called.append('{} - {}'.format(identifier, method_name))
2954 return inner
2955
2956
2957class RecordingHandler(logging.NullHandler):
2958
2959 def __init__(self, *args, **kwargs):
2960 super(RecordingHandler, self).__init__(*args, **kwargs)
2961 self.records = []
2962
2963 def handle(self, record):
2964 """Keep track of all the emitted records."""
2965 self.records.append(record)
2966
2967
2968class ShutdownTest(BaseTest):
2969
Vinay Sajip5e66b162011-04-20 15:41:14 +01002970 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002971
2972 def setUp(self):
2973 super(ShutdownTest, self).setUp()
2974 self.called = []
2975
2976 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002977 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002978
2979 def raise_error(self, error):
2980 def inner():
2981 raise error()
2982 return inner
2983
2984 def test_no_failure(self):
2985 # create some fake handlers
2986 handler0 = FakeHandler(0, self.called)
2987 handler1 = FakeHandler(1, self.called)
2988 handler2 = FakeHandler(2, self.called)
2989
2990 # create live weakref to those handlers
2991 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2992
2993 logging.shutdown(handlerList=list(handlers))
2994
2995 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2996 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2997 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2998 self.assertEqual(expected, self.called)
2999
3000 def _test_with_failure_in_method(self, method, error):
3001 handler = FakeHandler(0, self.called)
3002 setattr(handler, method, self.raise_error(error))
3003 handlers = [logging.weakref.ref(handler)]
3004
3005 logging.shutdown(handlerList=list(handlers))
3006
3007 self.assertEqual('0 - release', self.called[-1])
3008
3009 def test_with_ioerror_in_acquire(self):
3010 self._test_with_failure_in_method('acquire', IOError)
3011
3012 def test_with_ioerror_in_flush(self):
3013 self._test_with_failure_in_method('flush', IOError)
3014
3015 def test_with_ioerror_in_close(self):
3016 self._test_with_failure_in_method('close', IOError)
3017
3018 def test_with_valueerror_in_acquire(self):
3019 self._test_with_failure_in_method('acquire', ValueError)
3020
3021 def test_with_valueerror_in_flush(self):
3022 self._test_with_failure_in_method('flush', ValueError)
3023
3024 def test_with_valueerror_in_close(self):
3025 self._test_with_failure_in_method('close', ValueError)
3026
3027 def test_with_other_error_in_acquire_without_raise(self):
3028 logging.raiseExceptions = False
3029 self._test_with_failure_in_method('acquire', IndexError)
3030
3031 def test_with_other_error_in_flush_without_raise(self):
3032 logging.raiseExceptions = False
3033 self._test_with_failure_in_method('flush', IndexError)
3034
3035 def test_with_other_error_in_close_without_raise(self):
3036 logging.raiseExceptions = False
3037 self._test_with_failure_in_method('close', IndexError)
3038
3039 def test_with_other_error_in_acquire_with_raise(self):
3040 logging.raiseExceptions = True
3041 self.assertRaises(IndexError, self._test_with_failure_in_method,
3042 'acquire', IndexError)
3043
3044 def test_with_other_error_in_flush_with_raise(self):
3045 logging.raiseExceptions = True
3046 self.assertRaises(IndexError, self._test_with_failure_in_method,
3047 'flush', IndexError)
3048
3049 def test_with_other_error_in_close_with_raise(self):
3050 logging.raiseExceptions = True
3051 self.assertRaises(IndexError, self._test_with_failure_in_method,
3052 'close', IndexError)
3053
3054
3055class ModuleLevelMiscTest(BaseTest):
3056
Vinay Sajip5e66b162011-04-20 15:41:14 +01003057 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003058
3059 def test_disable(self):
3060 old_disable = logging.root.manager.disable
3061 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003062 self.assertEqual(old_disable, 0)
3063 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003064
3065 logging.disable(83)
3066 self.assertEqual(logging.root.manager.disable, 83)
3067
3068 def _test_log(self, method, level=None):
3069 called = []
3070 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003071 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003072
3073 recording = RecordingHandler()
3074 logging.root.addHandler(recording)
3075
3076 log_method = getattr(logging, method)
3077 if level is not None:
3078 log_method(level, "test me: %r", recording)
3079 else:
3080 log_method("test me: %r", recording)
3081
3082 self.assertEqual(len(recording.records), 1)
3083 record = recording.records[0]
3084 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3085
3086 expected_level = level if level is not None else getattr(logging, method.upper())
3087 self.assertEqual(record.levelno, expected_level)
3088
3089 # basicConfig was not called!
3090 self.assertEqual(called, [])
3091
3092 def test_log(self):
3093 self._test_log('log', logging.ERROR)
3094
3095 def test_debug(self):
3096 self._test_log('debug')
3097
3098 def test_info(self):
3099 self._test_log('info')
3100
3101 def test_warning(self):
3102 self._test_log('warning')
3103
3104 def test_error(self):
3105 self._test_log('error')
3106
3107 def test_critical(self):
3108 self._test_log('critical')
3109
3110 def test_set_logger_class(self):
3111 self.assertRaises(TypeError, logging.setLoggerClass, object)
3112
3113 class MyLogger(logging.Logger):
3114 pass
3115
3116 logging.setLoggerClass(MyLogger)
3117 self.assertEqual(logging.getLoggerClass(), MyLogger)
3118
3119 logging.setLoggerClass(logging.Logger)
3120 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3121
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003122class LogRecordTest(BaseTest):
3123 def test_str_rep(self):
3124 r = logging.makeLogRecord({})
3125 s = str(r)
3126 self.assertTrue(s.startswith('<LogRecord: '))
3127 self.assertTrue(s.endswith('>'))
3128
3129 def test_dict_arg(self):
3130 h = RecordingHandler()
3131 r = logging.getLogger()
3132 r.addHandler(h)
3133 d = {'less' : 'more' }
3134 logging.warning('less is %(less)s', d)
3135 self.assertIs(h.records[0].args, d)
3136 self.assertEqual(h.records[0].message, 'less is more')
3137 r.removeHandler(h)
3138 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003139
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003140 def test_multiprocessing(self):
3141 r = logging.makeLogRecord({})
3142 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003143 try:
3144 import multiprocessing as mp
3145 r = logging.makeLogRecord({})
3146 self.assertEqual(r.processName, mp.current_process().name)
3147 except ImportError:
3148 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003149
3150 def test_optional(self):
3151 r = logging.makeLogRecord({})
3152 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003153 if threading:
3154 NOT_NONE(r.thread)
3155 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003156 NOT_NONE(r.process)
3157 NOT_NONE(r.processName)
3158 log_threads = logging.logThreads
3159 log_processes = logging.logProcesses
3160 log_multiprocessing = logging.logMultiprocessing
3161 try:
3162 logging.logThreads = False
3163 logging.logProcesses = False
3164 logging.logMultiprocessing = False
3165 r = logging.makeLogRecord({})
3166 NONE = self.assertIsNone
3167 NONE(r.thread)
3168 NONE(r.threadName)
3169 NONE(r.process)
3170 NONE(r.processName)
3171 finally:
3172 logging.logThreads = log_threads
3173 logging.logProcesses = log_processes
3174 logging.logMultiprocessing = log_multiprocessing
3175
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003176class BasicConfigTest(unittest.TestCase):
3177
Vinay Sajip95bf5042011-04-20 11:50:56 +01003178 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003179
3180 def setUp(self):
3181 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003182 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003183 self.saved_handlers = logging._handlers.copy()
3184 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003185 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003186 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003187 logging.root.handlers = []
3188
3189 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003190 for h in logging.root.handlers[:]:
3191 logging.root.removeHandler(h)
3192 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003193 super(BasicConfigTest, self).tearDown()
3194
Vinay Sajip3def7e02011-04-20 10:58:06 +01003195 def cleanup(self):
3196 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003197 logging._handlers.clear()
3198 logging._handlers.update(self.saved_handlers)
3199 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003200 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003201
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003202 def test_no_kwargs(self):
3203 logging.basicConfig()
3204
3205 # handler defaults to a StreamHandler to sys.stderr
3206 self.assertEqual(len(logging.root.handlers), 1)
3207 handler = logging.root.handlers[0]
3208 self.assertIsInstance(handler, logging.StreamHandler)
3209 self.assertEqual(handler.stream, sys.stderr)
3210
3211 formatter = handler.formatter
3212 # format defaults to logging.BASIC_FORMAT
3213 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3214 # datefmt defaults to None
3215 self.assertIsNone(formatter.datefmt)
3216 # style defaults to %
3217 self.assertIsInstance(formatter._style, logging.PercentStyle)
3218
3219 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003220 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003221
3222 def test_filename(self):
3223 logging.basicConfig(filename='test.log')
3224
3225 self.assertEqual(len(logging.root.handlers), 1)
3226 handler = logging.root.handlers[0]
3227 self.assertIsInstance(handler, logging.FileHandler)
3228
3229 expected = logging.FileHandler('test.log', 'a')
3230 self.addCleanup(expected.close)
3231 self.assertEqual(handler.stream.mode, expected.stream.mode)
3232 self.assertEqual(handler.stream.name, expected.stream.name)
3233
3234 def test_filemode(self):
3235 logging.basicConfig(filename='test.log', filemode='wb')
3236
3237 handler = logging.root.handlers[0]
3238 expected = logging.FileHandler('test.log', 'wb')
3239 self.addCleanup(expected.close)
3240 self.assertEqual(handler.stream.mode, expected.stream.mode)
3241
3242 def test_stream(self):
3243 stream = io.StringIO()
3244 self.addCleanup(stream.close)
3245 logging.basicConfig(stream=stream)
3246
3247 self.assertEqual(len(logging.root.handlers), 1)
3248 handler = logging.root.handlers[0]
3249 self.assertIsInstance(handler, logging.StreamHandler)
3250 self.assertEqual(handler.stream, stream)
3251
3252 def test_format(self):
3253 logging.basicConfig(format='foo')
3254
3255 formatter = logging.root.handlers[0].formatter
3256 self.assertEqual(formatter._style._fmt, 'foo')
3257
3258 def test_datefmt(self):
3259 logging.basicConfig(datefmt='bar')
3260
3261 formatter = logging.root.handlers[0].formatter
3262 self.assertEqual(formatter.datefmt, 'bar')
3263
3264 def test_style(self):
3265 logging.basicConfig(style='$')
3266
3267 formatter = logging.root.handlers[0].formatter
3268 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3269
3270 def test_level(self):
3271 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003272 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003273
3274 logging.basicConfig(level=57)
3275 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003276 # Test that second call has no effect
3277 logging.basicConfig(level=58)
3278 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003279
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003280 def test_incompatible(self):
3281 assertRaises = self.assertRaises
3282 handlers = [logging.StreamHandler()]
3283 stream = sys.stderr
3284 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3285 stream=stream)
3286 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3287 handlers=handlers)
3288 assertRaises(ValueError, logging.basicConfig, stream=stream,
3289 handlers=handlers)
3290
3291 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003292 handlers = [
3293 logging.StreamHandler(),
3294 logging.StreamHandler(sys.stdout),
3295 logging.StreamHandler(),
3296 ]
3297 f = logging.Formatter()
3298 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003299 logging.basicConfig(handlers=handlers)
3300 self.assertIs(handlers[0], logging.root.handlers[0])
3301 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003302 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003303 self.assertIsNotNone(handlers[0].formatter)
3304 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003305 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003306 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3307
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003308 def _test_log(self, method, level=None):
3309 # logging.root has no handlers so basicConfig should be called
3310 called = []
3311
3312 old_basic_config = logging.basicConfig
3313 def my_basic_config(*a, **kw):
3314 old_basic_config()
3315 old_level = logging.root.level
3316 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003317 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003318 called.append((a, kw))
3319
3320 patch(self, logging, 'basicConfig', my_basic_config)
3321
3322 log_method = getattr(logging, method)
3323 if level is not None:
3324 log_method(level, "test me")
3325 else:
3326 log_method("test me")
3327
3328 # basicConfig was called with no arguments
3329 self.assertEqual(called, [((), {})])
3330
3331 def test_log(self):
3332 self._test_log('log', logging.WARNING)
3333
3334 def test_debug(self):
3335 self._test_log('debug')
3336
3337 def test_info(self):
3338 self._test_log('info')
3339
3340 def test_warning(self):
3341 self._test_log('warning')
3342
3343 def test_error(self):
3344 self._test_log('error')
3345
3346 def test_critical(self):
3347 self._test_log('critical')
3348
3349
3350class LoggerAdapterTest(unittest.TestCase):
3351
3352 def setUp(self):
3353 super(LoggerAdapterTest, self).setUp()
3354 old_handler_list = logging._handlerList[:]
3355
3356 self.recording = RecordingHandler()
3357 self.logger = logging.root
3358 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003359 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003360 self.addCleanup(self.recording.close)
3361
3362 def cleanup():
3363 logging._handlerList[:] = old_handler_list
3364
3365 self.addCleanup(cleanup)
3366 self.addCleanup(logging.shutdown)
3367 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3368
3369 def test_exception(self):
3370 msg = 'testing exception: %r'
3371 exc = None
3372 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003373 1 / 0
3374 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003375 exc = e
3376 self.adapter.exception(msg, self.recording)
3377
3378 self.assertEqual(len(self.recording.records), 1)
3379 record = self.recording.records[0]
3380 self.assertEqual(record.levelno, logging.ERROR)
3381 self.assertEqual(record.msg, msg)
3382 self.assertEqual(record.args, (self.recording,))
3383 self.assertEqual(record.exc_info,
3384 (exc.__class__, exc, exc.__traceback__))
3385
3386 def test_critical(self):
3387 msg = 'critical test! %r'
3388 self.adapter.critical(msg, self.recording)
3389
3390 self.assertEqual(len(self.recording.records), 1)
3391 record = self.recording.records[0]
3392 self.assertEqual(record.levelno, logging.CRITICAL)
3393 self.assertEqual(record.msg, msg)
3394 self.assertEqual(record.args, (self.recording,))
3395
3396 def test_is_enabled_for(self):
3397 old_disable = self.adapter.logger.manager.disable
3398 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003399 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3400 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003401 self.assertFalse(self.adapter.isEnabledFor(32))
3402
3403 def test_has_handlers(self):
3404 self.assertTrue(self.adapter.hasHandlers())
3405
3406 for handler in self.logger.handlers:
3407 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003408
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003409 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003410 self.assertFalse(self.adapter.hasHandlers())
3411
3412
3413class LoggerTest(BaseTest):
3414
3415 def setUp(self):
3416 super(LoggerTest, self).setUp()
3417 self.recording = RecordingHandler()
3418 self.logger = logging.Logger(name='blah')
3419 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003420 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003421 self.addCleanup(self.recording.close)
3422 self.addCleanup(logging.shutdown)
3423
3424 def test_set_invalid_level(self):
3425 self.assertRaises(TypeError, self.logger.setLevel, object())
3426
3427 def test_exception(self):
3428 msg = 'testing exception: %r'
3429 exc = None
3430 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003431 1 / 0
3432 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003433 exc = e
3434 self.logger.exception(msg, self.recording)
3435
3436 self.assertEqual(len(self.recording.records), 1)
3437 record = self.recording.records[0]
3438 self.assertEqual(record.levelno, logging.ERROR)
3439 self.assertEqual(record.msg, msg)
3440 self.assertEqual(record.args, (self.recording,))
3441 self.assertEqual(record.exc_info,
3442 (exc.__class__, exc, exc.__traceback__))
3443
3444 def test_log_invalid_level_with_raise(self):
3445 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003446 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003447
3448 logging.raiseExceptions = True
3449 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3450
3451 def test_log_invalid_level_no_raise(self):
3452 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003453 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003454
3455 logging.raiseExceptions = False
3456 self.logger.log('10', 'test message') # no exception happens
3457
3458 def test_find_caller_with_stack_info(self):
3459 called = []
3460 patch(self, logging.traceback, 'print_stack',
3461 lambda f, file: called.append(file.getvalue()))
3462
3463 self.logger.findCaller(stack_info=True)
3464
3465 self.assertEqual(len(called), 1)
3466 self.assertEqual('Stack (most recent call last):\n', called[0])
3467
3468 def test_make_record_with_extra_overwrite(self):
3469 name = 'my record'
3470 level = 13
3471 fn = lno = msg = args = exc_info = func = sinfo = None
3472 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3473 exc_info, func, sinfo)
3474
3475 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3476 extra = {key: 'some value'}
3477 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3478 fn, lno, msg, args, exc_info,
3479 extra=extra, sinfo=sinfo)
3480
3481 def test_make_record_with_extra_no_overwrite(self):
3482 name = 'my record'
3483 level = 13
3484 fn = lno = msg = args = exc_info = func = sinfo = None
3485 extra = {'valid_key': 'some value'}
3486 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3487 exc_info, extra=extra, sinfo=sinfo)
3488 self.assertIn('valid_key', result.__dict__)
3489
3490 def test_has_handlers(self):
3491 self.assertTrue(self.logger.hasHandlers())
3492
3493 for handler in self.logger.handlers:
3494 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003495 self.assertFalse(self.logger.hasHandlers())
3496
3497 def test_has_handlers_no_propagate(self):
3498 child_logger = logging.getLogger('blah.child')
3499 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003500 self.assertFalse(child_logger.hasHandlers())
3501
3502 def test_is_enabled_for(self):
3503 old_disable = self.logger.manager.disable
3504 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003505 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003506 self.assertFalse(self.logger.isEnabledFor(22))
3507
3508
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003509class BaseFileTest(BaseTest):
3510 "Base class for handler tests that write log files"
3511
3512 def setUp(self):
3513 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003514 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3515 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003516 self.rmfiles = []
3517
3518 def tearDown(self):
3519 for fn in self.rmfiles:
3520 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003521 if os.path.exists(self.fn):
3522 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003523 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003524
3525 def assertLogFile(self, filename):
3526 "Assert a log file is there and register it for deletion"
3527 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003528 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003529 self.rmfiles.append(filename)
3530
3531
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003532class FileHandlerTest(BaseFileTest):
3533 def test_delay(self):
3534 os.unlink(self.fn)
3535 fh = logging.FileHandler(self.fn, delay=True)
3536 self.assertIsNone(fh.stream)
3537 self.assertFalse(os.path.exists(self.fn))
3538 fh.handle(logging.makeLogRecord({}))
3539 self.assertIsNotNone(fh.stream)
3540 self.assertTrue(os.path.exists(self.fn))
3541 fh.close()
3542
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003543class RotatingFileHandlerTest(BaseFileTest):
3544 def next_rec(self):
3545 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3546 self.next_message(), None, None, None)
3547
3548 def test_should_not_rollover(self):
3549 # If maxbytes is zero rollover never occurs
3550 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3551 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003552 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003553
3554 def test_should_rollover(self):
3555 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3556 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003557 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003558
3559 def test_file_created(self):
3560 # checks that the file is created and assumes it was created
3561 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003562 rh = logging.handlers.RotatingFileHandler(self.fn)
3563 rh.emit(self.next_rec())
3564 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003565 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003566
3567 def test_rollover_filenames(self):
3568 rh = logging.handlers.RotatingFileHandler(
3569 self.fn, backupCount=2, maxBytes=1)
3570 rh.emit(self.next_rec())
3571 self.assertLogFile(self.fn)
3572 rh.emit(self.next_rec())
3573 self.assertLogFile(self.fn + ".1")
3574 rh.emit(self.next_rec())
3575 self.assertLogFile(self.fn + ".2")
3576 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003577 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003578
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003579class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003580 # other test methods added below
3581 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003582 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3583 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003584 r = logging.makeLogRecord({'msg': 'testing'})
3585 fh.emit(r)
3586 self.assertLogFile(self.fn)
Vinay Sajipb7b19302011-05-24 07:12:41 +01003587 time.sleep(1.01) # just a little over a second ...
Vinay Sajipfd285022011-05-07 17:01:22 +01003588 fh.emit(r)
3589 fh.close()
3590 # At this point, we should have a recent rotated file which we
3591 # can test for the existence of. However, in practice, on some
3592 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003593 # in time to go to look for the log file. So, we go back a fair
3594 # bit, and stop as soon as we see a rotated file. In theory this
3595 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003596 found = False
3597 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003598 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003599 for secs in range(GO_BACK):
3600 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003601 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3602 found = os.path.exists(fn)
3603 if found:
3604 self.rmfiles.append(fn)
3605 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003606 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3607 if not found:
3608 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003609 dn, fn = os.path.split(self.fn)
3610 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003611 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3612 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajipfd285022011-05-07 17:01:22 +01003613 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003614
Vinay Sajip0372e102011-05-05 12:59:14 +01003615 def test_invalid(self):
3616 assertRaises = self.assertRaises
3617 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003618 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003619 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003620 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003621 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003622 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003623
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003624def secs(**kw):
3625 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3626
3627for when, exp in (('S', 1),
3628 ('M', 60),
3629 ('H', 60 * 60),
3630 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003631 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003632 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003633 ('W0', secs(days=4, hours=24)),
3634 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003635 def test_compute_rollover(self, when=when, exp=exp):
3636 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003637 self.fn, when=when, interval=1, backupCount=0, utc=True)
3638 currentTime = 0.0
3639 actual = rh.computeRollover(currentTime)
3640 if exp != actual:
3641 # Failures occur on some systems for MIDNIGHT and W0.
3642 # Print detailed calculation for MIDNIGHT so we can try to see
3643 # what's going on
3644 import time
3645 if when == 'MIDNIGHT':
3646 try:
3647 if rh.utc:
3648 t = time.gmtime(currentTime)
3649 else:
3650 t = time.localtime(currentTime)
3651 currentHour = t[3]
3652 currentMinute = t[4]
3653 currentSecond = t[5]
3654 # r is the number of seconds left between now and midnight
3655 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3656 currentMinute) * 60 +
3657 currentSecond)
3658 result = currentTime + r
3659 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3660 print('currentHour: %s' % currentHour, file=sys.stderr)
3661 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3662 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3663 print('r: %s' % r, file=sys.stderr)
3664 print('result: %s' % result, file=sys.stderr)
3665 except Exception:
3666 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3667 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003668 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003669 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3670
Vinay Sajip60ccd822011-05-09 17:32:09 +01003671
3672@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3673class NTEventLogHandlerTest(BaseTest):
3674 def test_basic(self):
3675 logtype = 'Application'
3676 elh = win32evtlog.OpenEventLog(None, logtype)
3677 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3678 h = logging.handlers.NTEventLogHandler('test_logging')
3679 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3680 h.handle(r)
3681 h.close()
3682 # Now see if the event is recorded
3683 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3684 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3685 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3686 found = False
3687 GO_BACK = 100
3688 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3689 for e in events:
3690 if e.SourceName != 'test_logging':
3691 continue
3692 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3693 if msg != 'Test Log Message\r\n':
3694 continue
3695 found = True
3696 break
3697 msg = 'Record not found in event log, went back %d records' % GO_BACK
3698 self.assertTrue(found, msg=msg)
3699
Christian Heimes180510d2008-03-03 19:15:45 +00003700# Set the locale to the platform-dependent default. I have no idea
3701# why the test does this, but in any case we save the current locale
3702# first and restore it at the end.
3703@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003704def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003705 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003706 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003707 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3708 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3709 ManagerTest, FormatterTest, BufferingFormatterTest,
3710 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3711 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3712 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3713 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003714 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003715 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003716 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003717 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003718
Christian Heimes180510d2008-03-03 19:15:45 +00003719if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003720 test_main()