blob: edcefa1406fc7ff6509def17e4e9939ab1411643 [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
Eric V. Smith851cad72012-03-11 22:46:04 -070042from test.support import (captured_stdout, run_with_locale, run_unittest,
43 patch, requires_zlib, 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
Florent Xicluna5252f9f2011-11-07 19:43:05 +010052 # require threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010053 import asynchat
54 import asyncore
55 import errno
56 from http.server import HTTPServer, BaseHTTPRequestHandler
57 import smtpd
58 from urllib.parse import urlparse, parse_qs
59 from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
60 ThreadingTCPServer, StreamRequestHandler)
Victor Stinner45df8202010-04-28 22:31:17 +000061except ImportError:
62 threading = None
Vinay Sajip60ccd822011-05-09 17:32:09 +010063try:
64 import win32evtlog
65except ImportError:
66 win32evtlog = None
67try:
68 import win32evtlogutil
69except ImportError:
70 win32evtlogutil = None
71 win32evtlog = None
Eric V. Smith851cad72012-03-11 22:46:04 -070072try:
73 import zlib
74except ImportError:
75 pass
Christian Heimes18c66892008-02-17 13:31:39 +000076
77
Christian Heimes180510d2008-03-03 19:15:45 +000078class BaseTest(unittest.TestCase):
79
80 """Base class for logging tests."""
81
82 log_format = "%(name)s -> %(levelname)s: %(message)s"
83 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
84 message_num = 0
85
86 def setUp(self):
87 """Setup the default logging stream to an internal StringIO instance,
88 so that we can examine log output as we want."""
89 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000090 logging._acquireLock()
91 try:
Christian Heimes180510d2008-03-03 19:15:45 +000092 self.saved_handlers = logging._handlers.copy()
93 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000094 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000095 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000096 self.logger_states = logger_states = {}
97 for name in saved_loggers:
98 logger_states[name] = getattr(saved_loggers[name],
99 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +0000100 finally:
101 logging._releaseLock()
102
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100103 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +0000104 self.logger1 = logging.getLogger("\xab\xd7\xbb")
105 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000106
Christian Heimes180510d2008-03-03 19:15:45 +0000107 self.root_logger = logging.getLogger("")
108 self.original_logging_level = self.root_logger.getEffectiveLevel()
109
110 self.stream = io.StringIO()
111 self.root_logger.setLevel(logging.DEBUG)
112 self.root_hdlr = logging.StreamHandler(self.stream)
113 self.root_formatter = logging.Formatter(self.log_format)
114 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000115 if self.logger1.hasHandlers():
116 hlist = self.logger1.handlers + self.root_logger.handlers
117 raise AssertionError('Unexpected handlers: %s' % hlist)
118 if self.logger2.hasHandlers():
119 hlist = self.logger2.handlers + self.root_logger.handlers
120 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000121 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000122 self.assertTrue(self.logger1.hasHandlers())
123 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000124
125 def tearDown(self):
126 """Remove our logging stream, and restore the original logging
127 level."""
128 self.stream.close()
129 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000130 while self.root_logger.handlers:
131 h = self.root_logger.handlers[0]
132 self.root_logger.removeHandler(h)
133 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000134 self.root_logger.setLevel(self.original_logging_level)
135 logging._acquireLock()
136 try:
137 logging._levelNames.clear()
138 logging._levelNames.update(self.saved_level_names)
139 logging._handlers.clear()
140 logging._handlers.update(self.saved_handlers)
141 logging._handlerList[:] = self.saved_handler_list
142 loggerDict = logging.getLogger().manager.loggerDict
143 loggerDict.clear()
144 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000145 logger_states = self.logger_states
146 for name in self.logger_states:
147 if logger_states[name] is not None:
148 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000149 finally:
150 logging._releaseLock()
151
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000152 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000153 """Match the collected log lines against the regular expression
154 self.expected_log_pat, and compare the extracted group values to
155 the expected_values list of tuples."""
156 stream = stream or self.stream
157 pat = re.compile(self.expected_log_pat)
158 try:
159 stream.reset()
160 actual_lines = stream.readlines()
161 except AttributeError:
162 # StringIO.StringIO lacks a reset() method.
163 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100164 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000165 for actual, expected in zip(actual_lines, expected_values):
166 match = pat.search(actual)
167 if not match:
168 self.fail("Log line does not match expected pattern:\n" +
169 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000170 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000171 s = stream.read()
172 if s:
173 self.fail("Remaining output at end of log stream:\n" + s)
174
175 def next_message(self):
176 """Generate a message consisting solely of an auto-incrementing
177 integer."""
178 self.message_num += 1
179 return "%d" % self.message_num
180
181
182class BuiltinLevelsTest(BaseTest):
183 """Test builtin levels and their inheritance."""
184
185 def test_flat(self):
186 #Logging levels in a flat logger namespace.
187 m = self.next_message
188
189 ERR = logging.getLogger("ERR")
190 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000191 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000192 INF.setLevel(logging.INFO)
193 DEB = logging.getLogger("DEB")
194 DEB.setLevel(logging.DEBUG)
195
196 # These should log.
197 ERR.log(logging.CRITICAL, m())
198 ERR.error(m())
199
200 INF.log(logging.CRITICAL, m())
201 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100202 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000203 INF.info(m())
204
205 DEB.log(logging.CRITICAL, m())
206 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100207 DEB.warning(m())
208 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000209 DEB.debug(m())
210
211 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100212 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000213 ERR.info(m())
214 ERR.debug(m())
215
216 INF.debug(m())
217
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000218 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000219 ('ERR', 'CRITICAL', '1'),
220 ('ERR', 'ERROR', '2'),
221 ('INF', 'CRITICAL', '3'),
222 ('INF', 'ERROR', '4'),
223 ('INF', 'WARNING', '5'),
224 ('INF', 'INFO', '6'),
225 ('DEB', 'CRITICAL', '7'),
226 ('DEB', 'ERROR', '8'),
227 ('DEB', 'WARNING', '9'),
228 ('DEB', 'INFO', '10'),
229 ('DEB', 'DEBUG', '11'),
230 ])
231
232 def test_nested_explicit(self):
233 # Logging levels in a nested namespace, all explicitly set.
234 m = self.next_message
235
236 INF = logging.getLogger("INF")
237 INF.setLevel(logging.INFO)
238 INF_ERR = logging.getLogger("INF.ERR")
239 INF_ERR.setLevel(logging.ERROR)
240
241 # These should log.
242 INF_ERR.log(logging.CRITICAL, m())
243 INF_ERR.error(m())
244
245 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100246 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000247 INF_ERR.info(m())
248 INF_ERR.debug(m())
249
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000250 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000251 ('INF.ERR', 'CRITICAL', '1'),
252 ('INF.ERR', 'ERROR', '2'),
253 ])
254
255 def test_nested_inherited(self):
256 #Logging levels in a nested namespace, inherited from parent loggers.
257 m = self.next_message
258
259 INF = logging.getLogger("INF")
260 INF.setLevel(logging.INFO)
261 INF_ERR = logging.getLogger("INF.ERR")
262 INF_ERR.setLevel(logging.ERROR)
263 INF_UNDEF = logging.getLogger("INF.UNDEF")
264 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
265 UNDEF = logging.getLogger("UNDEF")
266
267 # These should log.
268 INF_UNDEF.log(logging.CRITICAL, m())
269 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100270 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000271 INF_UNDEF.info(m())
272 INF_ERR_UNDEF.log(logging.CRITICAL, m())
273 INF_ERR_UNDEF.error(m())
274
275 # These should not log.
276 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100277 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000278 INF_ERR_UNDEF.info(m())
279 INF_ERR_UNDEF.debug(m())
280
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000281 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000282 ('INF.UNDEF', 'CRITICAL', '1'),
283 ('INF.UNDEF', 'ERROR', '2'),
284 ('INF.UNDEF', 'WARNING', '3'),
285 ('INF.UNDEF', 'INFO', '4'),
286 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
287 ('INF.ERR.UNDEF', 'ERROR', '6'),
288 ])
289
290 def test_nested_with_virtual_parent(self):
291 # Logging levels when some parent does not exist yet.
292 m = self.next_message
293
294 INF = logging.getLogger("INF")
295 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
296 CHILD = logging.getLogger("INF.BADPARENT")
297 INF.setLevel(logging.INFO)
298
299 # These should log.
300 GRANDCHILD.log(logging.FATAL, m())
301 GRANDCHILD.info(m())
302 CHILD.log(logging.FATAL, m())
303 CHILD.info(m())
304
305 # These should not log.
306 GRANDCHILD.debug(m())
307 CHILD.debug(m())
308
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000309 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000310 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
311 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
312 ('INF.BADPARENT', 'CRITICAL', '3'),
313 ('INF.BADPARENT', 'INFO', '4'),
314 ])
315
316
317class BasicFilterTest(BaseTest):
318
319 """Test the bundled Filter class."""
320
321 def test_filter(self):
322 # Only messages satisfying the specified criteria pass through the
323 # filter.
324 filter_ = logging.Filter("spam.eggs")
325 handler = self.root_logger.handlers[0]
326 try:
327 handler.addFilter(filter_)
328 spam = logging.getLogger("spam")
329 spam_eggs = logging.getLogger("spam.eggs")
330 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
331 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
332
333 spam.info(self.next_message())
334 spam_eggs.info(self.next_message()) # Good.
335 spam_eggs_fish.info(self.next_message()) # Good.
336 spam_bakedbeans.info(self.next_message())
337
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000338 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000339 ('spam.eggs', 'INFO', '2'),
340 ('spam.eggs.fish', 'INFO', '3'),
341 ])
342 finally:
343 handler.removeFilter(filter_)
344
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000345 def test_callable_filter(self):
346 # Only messages satisfying the specified criteria pass through the
347 # filter.
348
349 def filterfunc(record):
350 parts = record.name.split('.')
351 prefix = '.'.join(parts[:2])
352 return prefix == 'spam.eggs'
353
354 handler = self.root_logger.handlers[0]
355 try:
356 handler.addFilter(filterfunc)
357 spam = logging.getLogger("spam")
358 spam_eggs = logging.getLogger("spam.eggs")
359 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
360 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
361
362 spam.info(self.next_message())
363 spam_eggs.info(self.next_message()) # Good.
364 spam_eggs_fish.info(self.next_message()) # Good.
365 spam_bakedbeans.info(self.next_message())
366
367 self.assert_log_lines([
368 ('spam.eggs', 'INFO', '2'),
369 ('spam.eggs.fish', 'INFO', '3'),
370 ])
371 finally:
372 handler.removeFilter(filterfunc)
373
Vinay Sajip985ef872011-04-26 19:34:04 +0100374 def test_empty_filter(self):
375 f = logging.Filter()
376 r = logging.makeLogRecord({'name': 'spam.eggs'})
377 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000378
379#
380# First, we define our levels. There can be as many as you want - the only
381# limitations are that they should be integers, the lowest should be > 0 and
382# larger values mean less information being logged. If you need specific
383# level values which do not fit into these limitations, you can use a
384# mapping dictionary to convert between your application levels and the
385# logging system.
386#
387SILENT = 120
388TACITURN = 119
389TERSE = 118
390EFFUSIVE = 117
391SOCIABLE = 116
392VERBOSE = 115
393TALKATIVE = 114
394GARRULOUS = 113
395CHATTERBOX = 112
396BORING = 111
397
398LEVEL_RANGE = range(BORING, SILENT + 1)
399
400#
401# Next, we define names for our levels. You don't need to do this - in which
402# case the system will use "Level n" to denote the text for the level.
403#
404my_logging_levels = {
405 SILENT : 'Silent',
406 TACITURN : 'Taciturn',
407 TERSE : 'Terse',
408 EFFUSIVE : 'Effusive',
409 SOCIABLE : 'Sociable',
410 VERBOSE : 'Verbose',
411 TALKATIVE : 'Talkative',
412 GARRULOUS : 'Garrulous',
413 CHATTERBOX : 'Chatterbox',
414 BORING : 'Boring',
415}
416
417class GarrulousFilter(logging.Filter):
418
419 """A filter which blocks garrulous messages."""
420
421 def filter(self, record):
422 return record.levelno != GARRULOUS
423
424class VerySpecificFilter(logging.Filter):
425
426 """A filter which blocks sociable and taciturn messages."""
427
428 def filter(self, record):
429 return record.levelno not in [SOCIABLE, TACITURN]
430
431
432class CustomLevelsAndFiltersTest(BaseTest):
433
434 """Test various filtering possibilities with custom logging levels."""
435
436 # Skip the logger name group.
437 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
438
439 def setUp(self):
440 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000441 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000442 logging.addLevelName(k, v)
443
444 def log_at_all_levels(self, logger):
445 for lvl in LEVEL_RANGE:
446 logger.log(lvl, self.next_message())
447
448 def test_logger_filter(self):
449 # Filter at logger level.
450 self.root_logger.setLevel(VERBOSE)
451 # Levels >= 'Verbose' are good.
452 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000453 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000454 ('Verbose', '5'),
455 ('Sociable', '6'),
456 ('Effusive', '7'),
457 ('Terse', '8'),
458 ('Taciturn', '9'),
459 ('Silent', '10'),
460 ])
461
462 def test_handler_filter(self):
463 # Filter at handler level.
464 self.root_logger.handlers[0].setLevel(SOCIABLE)
465 try:
466 # Levels >= 'Sociable' are good.
467 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000468 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000469 ('Sociable', '6'),
470 ('Effusive', '7'),
471 ('Terse', '8'),
472 ('Taciturn', '9'),
473 ('Silent', '10'),
474 ])
475 finally:
476 self.root_logger.handlers[0].setLevel(logging.NOTSET)
477
478 def test_specific_filters(self):
479 # Set a specific filter object on the handler, and then add another
480 # filter object on the logger itself.
481 handler = self.root_logger.handlers[0]
482 specific_filter = None
483 garr = GarrulousFilter()
484 handler.addFilter(garr)
485 try:
486 self.log_at_all_levels(self.root_logger)
487 first_lines = [
488 # Notice how 'Garrulous' is missing
489 ('Boring', '1'),
490 ('Chatterbox', '2'),
491 ('Talkative', '4'),
492 ('Verbose', '5'),
493 ('Sociable', '6'),
494 ('Effusive', '7'),
495 ('Terse', '8'),
496 ('Taciturn', '9'),
497 ('Silent', '10'),
498 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000499 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000500
501 specific_filter = VerySpecificFilter()
502 self.root_logger.addFilter(specific_filter)
503 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000504 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000505 # Not only 'Garrulous' is still missing, but also 'Sociable'
506 # and 'Taciturn'
507 ('Boring', '11'),
508 ('Chatterbox', '12'),
509 ('Talkative', '14'),
510 ('Verbose', '15'),
511 ('Effusive', '17'),
512 ('Terse', '18'),
513 ('Silent', '20'),
514 ])
515 finally:
516 if specific_filter:
517 self.root_logger.removeFilter(specific_filter)
518 handler.removeFilter(garr)
519
520
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100521class HandlerTest(BaseTest):
522 def test_name(self):
523 h = logging.Handler()
524 h.name = 'generic'
525 self.assertEqual(h.name, 'generic')
526 h.name = 'anothergeneric'
527 self.assertEqual(h.name, 'anothergeneric')
528 self.assertRaises(NotImplementedError, h.emit, None)
529
Vinay Sajip5a35b062011-04-27 11:31:14 +0100530 def test_builtin_handlers(self):
531 # We can't actually *use* too many handlers in the tests,
532 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200533 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100534 for existing in (True, False):
535 fd, fn = tempfile.mkstemp()
536 os.close(fd)
537 if not existing:
538 os.unlink(fn)
539 h = logging.handlers.WatchedFileHandler(fn, delay=True)
540 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100541 dev, ino = h.dev, h.ino
542 self.assertNotEqual(dev, -1)
543 self.assertNotEqual(ino, -1)
544 r = logging.makeLogRecord({'msg': 'Test'})
545 h.handle(r)
546 # Now remove the file.
547 os.unlink(fn)
548 self.assertFalse(os.path.exists(fn))
549 # The next call should recreate the file.
550 h.handle(r)
551 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100552 else:
553 self.assertEqual(h.dev, -1)
554 self.assertEqual(h.ino, -1)
555 h.close()
556 if existing:
557 os.unlink(fn)
558 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100559 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100560 else:
561 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100562 try:
563 h = logging.handlers.SysLogHandler(sockname)
564 self.assertEqual(h.facility, h.LOG_USER)
565 self.assertTrue(h.unixsocket)
566 h.close()
567 except socket.error: # syslogd might not be available
568 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100569 for method in ('GET', 'POST', 'PUT'):
570 if method == 'PUT':
571 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
572 'localhost', '/log', method)
573 else:
574 h = logging.handlers.HTTPHandler('localhost', '/log', method)
575 h.close()
576 h = logging.handlers.BufferingHandler(0)
577 r = logging.makeLogRecord({})
578 self.assertTrue(h.shouldFlush(r))
579 h.close()
580 h = logging.handlers.BufferingHandler(1)
581 self.assertFalse(h.shouldFlush(r))
582 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100583
584class BadStream(object):
585 def write(self, data):
586 raise RuntimeError('deliberate mistake')
587
588class TestStreamHandler(logging.StreamHandler):
589 def handleError(self, record):
590 self.error_record = record
591
592class StreamHandlerTest(BaseTest):
593 def test_error_handling(self):
594 h = TestStreamHandler(BadStream())
595 r = logging.makeLogRecord({})
596 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100597 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100598 try:
599 h.handle(r)
600 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100601 h = logging.StreamHandler(BadStream())
602 sys.stderr = sio = io.StringIO()
603 h.handle(r)
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100604 self.assertIn('\nRuntimeError: deliberate mistake\n',
605 sio.getvalue())
Vinay Sajip985ef872011-04-26 19:34:04 +0100606 logging.raiseExceptions = False
607 sys.stderr = sio = io.StringIO()
608 h.handle(r)
609 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100610 finally:
611 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100612 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100613
Vinay Sajip7367d082011-05-02 13:17:27 +0100614# -- The following section could be moved into a server_helper.py module
615# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100616
Vinay Sajipce7c9782011-05-17 07:15:53 +0100617if threading:
618 class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100619 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100620 This derived class has had to be created because smtpd does not
621 support use of custom channel maps, although they are allowed by
622 asyncore's design. Issue #11959 has been raised to address this,
623 and if resolved satisfactorily, some of this code can be removed.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100624 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100625 def __init__(self, server, conn, addr, sockmap):
626 asynchat.async_chat.__init__(self, conn, sockmap)
627 self.smtp_server = server
628 self.conn = conn
629 self.addr = addr
630 self.received_lines = []
631 self.smtp_state = self.COMMAND
632 self.seen_greeting = ''
633 self.mailfrom = None
634 self.rcpttos = []
635 self.received_data = ''
636 self.fqdn = socket.getfqdn()
637 self.num_bytes = 0
638 try:
639 self.peer = conn.getpeername()
640 except socket.error as err:
641 # a race condition may occur if the other end is closing
642 # before we can get the peername
643 self.close()
644 if err.args[0] != errno.ENOTCONN:
645 raise
646 return
647 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
648 self.set_terminator(b'\r\n')
Vinay Sajipa463d252011-04-30 21:52:48 +0100649
Vinay Sajip314b92b2011-05-02 14:31:16 +0100650
Vinay Sajipce7c9782011-05-17 07:15:53 +0100651 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100652 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100653 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100654
Vinay Sajipce7c9782011-05-17 07:15:53 +0100655 :param addr: A (host, port) tuple which the server listens on.
656 You can specify a port value of zero: the server's
657 *port* attribute will hold the actual port number
658 used, which can be used in client connections.
659 :param handler: A callable which will be called to process
660 incoming messages. The handler will be passed
661 the client address tuple, who the message is from,
662 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100663 :param poll_interval: The interval, in seconds, used in the underlying
664 :func:`select` or :func:`poll` call by
665 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100666 :param sockmap: A dictionary which will be used to hold
667 :class:`asyncore.dispatcher` instances used by
668 :func:`asyncore.loop`. This avoids changing the
669 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100670 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100671 channel_class = TestSMTPChannel
672
673 def __init__(self, addr, handler, poll_interval, sockmap):
674 self._localaddr = addr
675 self._remoteaddr = None
676 self.sockmap = sockmap
677 asyncore.dispatcher.__init__(self, map=sockmap)
678 try:
679 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
680 sock.setblocking(0)
681 self.set_socket(sock, map=sockmap)
682 # try to re-use a server port if possible
683 self.set_reuse_addr()
684 self.bind(addr)
685 self.port = sock.getsockname()[1]
686 self.listen(5)
687 except:
688 self.close()
Vinay Sajipe73afad2011-05-10 07:48:28 +0100689 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +0100690 self._handler = handler
691 self._thread = None
692 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100693
Vinay Sajipce7c9782011-05-17 07:15:53 +0100694 def handle_accepted(self, conn, addr):
695 """
696 Redefined only because the base class does not pass in a
697 map, forcing use of a global in :mod:`asyncore`.
698 """
699 channel = self.channel_class(self, conn, addr, self.sockmap)
Vinay Sajip314b92b2011-05-02 14:31:16 +0100700
Vinay Sajipce7c9782011-05-17 07:15:53 +0100701 def process_message(self, peer, mailfrom, rcpttos, data):
702 """
703 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100704
Vinay Sajipce7c9782011-05-17 07:15:53 +0100705 Typically, this will be a test case method.
706 :param peer: The client (host, port) tuple.
707 :param mailfrom: The address of the sender.
708 :param rcpttos: The addresses of the recipients.
709 :param data: The message.
710 """
711 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100712
Vinay Sajipce7c9782011-05-17 07:15:53 +0100713 def start(self):
714 """
715 Start the server running on a separate daemon thread.
716 """
717 self._thread = t = threading.Thread(target=self.serve_forever,
718 args=(self.poll_interval,))
719 t.setDaemon(True)
720 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100721
Vinay Sajipce7c9782011-05-17 07:15:53 +0100722 def serve_forever(self, poll_interval):
723 """
724 Run the :mod:`asyncore` loop until normal termination
725 conditions arise.
726 :param poll_interval: The interval, in seconds, used in the underlying
727 :func:`select` or :func:`poll` call by
728 :func:`asyncore.loop`.
729 """
730 try:
731 asyncore.loop(poll_interval, map=self.sockmap)
732 except select.error:
733 # On FreeBSD 8, closing the server repeatably
734 # raises this error. We swallow it if the
735 # server has been closed.
736 if self.connected or self.accepting:
737 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100738
Vinay Sajipce7c9782011-05-17 07:15:53 +0100739 def stop(self, timeout=None):
740 """
741 Stop the thread by closing the server instance.
742 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100743
Vinay Sajipce7c9782011-05-17 07:15:53 +0100744 :param timeout: How long to wait for the server thread
745 to terminate.
746 """
747 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100748 self._thread.join(timeout)
749 self._thread = None
Vinay Sajip7367d082011-05-02 13:17:27 +0100750
Vinay Sajipce7c9782011-05-17 07:15:53 +0100751 class ControlMixin(object):
752 """
753 This mixin is used to start a server on a separate thread, and
754 shut it down programmatically. Request handling is simplified - instead
755 of needing to derive a suitable RequestHandler subclass, you just
756 provide a callable which will be passed each received request to be
757 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100758
Vinay Sajipce7c9782011-05-17 07:15:53 +0100759 :param handler: A handler callable which will be called with a
760 single parameter - the request - in order to
761 process the request. This handler is called on the
762 server thread, effectively meaning that requests are
763 processed serially. While not quite Web scale ;-),
764 this should be fine for testing applications.
765 :param poll_interval: The polling interval in seconds.
766 """
767 def __init__(self, handler, poll_interval):
768 self._thread = None
769 self.poll_interval = poll_interval
770 self._handler = handler
771 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100772
Vinay Sajipce7c9782011-05-17 07:15:53 +0100773 def start(self):
774 """
775 Create a daemon thread to run the server, and start it.
776 """
777 self._thread = t = threading.Thread(target=self.serve_forever,
778 args=(self.poll_interval,))
779 t.setDaemon(True)
780 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100781
Vinay Sajipce7c9782011-05-17 07:15:53 +0100782 def serve_forever(self, poll_interval):
783 """
784 Run the server. Set the ready flag before entering the
785 service loop.
786 """
787 self.ready.set()
788 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100789
Vinay Sajipce7c9782011-05-17 07:15:53 +0100790 def stop(self, timeout=None):
791 """
792 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100793
Vinay Sajipce7c9782011-05-17 07:15:53 +0100794 :param timeout: How long to wait for the server thread
795 to terminate.
796 """
797 self.shutdown()
798 if self._thread is not None:
799 self._thread.join(timeout)
800 self._thread = None
801 self.server_close()
802 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100803
Vinay Sajipce7c9782011-05-17 07:15:53 +0100804 class TestHTTPServer(ControlMixin, HTTPServer):
805 """
806 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100807
Vinay Sajipce7c9782011-05-17 07:15:53 +0100808 :param addr: A tuple with the IP address and port to listen on.
809 :param handler: A handler callable which will be called with a
810 single parameter - the request - in order to
811 process the request.
812 :param poll_interval: The polling interval in seconds.
813 :param log: Pass ``True`` to enable log messages.
814 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100815 def __init__(self, addr, handler, poll_interval=0.5,
816 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100817 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
818 def __getattr__(self, name, default=None):
819 if name.startswith('do_'):
820 return self.process_request
821 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100822
Vinay Sajipce7c9782011-05-17 07:15:53 +0100823 def process_request(self):
824 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100825
Vinay Sajipce7c9782011-05-17 07:15:53 +0100826 def log_message(self, format, *args):
827 if log:
828 super(DelegatingHTTPRequestHandler,
829 self).log_message(format, *args)
830 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
831 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100832 self.sslctx = sslctx
833
834 def get_request(self):
835 try:
836 sock, addr = self.socket.accept()
837 if self.sslctx:
838 sock = self.sslctx.wrap_socket(sock, server_side=True)
839 except socket.error as e:
840 # socket errors are silenced by the caller, print them here
841 sys.stderr.write("Got an error:\n%s\n" % e)
842 raise
843 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100844
Vinay Sajipce7c9782011-05-17 07:15:53 +0100845 class TestTCPServer(ControlMixin, ThreadingTCPServer):
846 """
847 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100848
Vinay Sajipce7c9782011-05-17 07:15:53 +0100849 :param addr: A tuple with the IP address and port to listen on.
850 :param handler: A handler callable which will be called with a single
851 parameter - the request - in order to process the request.
852 :param poll_interval: The polling interval in seconds.
853 :bind_and_activate: If True (the default), binds the server and starts it
854 listening. If False, you need to call
855 :meth:`server_bind` and :meth:`server_activate` at
856 some later time before calling :meth:`start`, so that
857 the server will set up the socket and listen on it.
858 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100859
Vinay Sajipce7c9782011-05-17 07:15:53 +0100860 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100861
Vinay Sajipce7c9782011-05-17 07:15:53 +0100862 def __init__(self, addr, handler, poll_interval=0.5,
863 bind_and_activate=True):
864 class DelegatingTCPRequestHandler(StreamRequestHandler):
865
866 def handle(self):
867 self.server._handler(self)
868 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
869 bind_and_activate)
870 ControlMixin.__init__(self, handler, poll_interval)
871
872 def server_bind(self):
873 super(TestTCPServer, self).server_bind()
874 self.port = self.socket.getsockname()[1]
875
876 class TestUDPServer(ControlMixin, ThreadingUDPServer):
877 """
878 A UDP server which is controllable using :class:`ControlMixin`.
879
880 :param addr: A tuple with the IP address and port to listen on.
881 :param handler: A handler callable which will be called with a
882 single parameter - the request - in order to
883 process the request.
884 :param poll_interval: The polling interval for shutdown requests,
885 in seconds.
886 :bind_and_activate: If True (the default), binds the server and
887 starts it listening. If False, you need to
888 call :meth:`server_bind` and
889 :meth:`server_activate` at some later time
890 before calling :meth:`start`, so that the server will
891 set up the socket and listen on it.
892 """
Vinay Sajip3ef12292011-05-23 23:00:42 +0100893 def __init__(self, addr, handler, poll_interval=0.5,
894 bind_and_activate=True):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100895 class DelegatingUDPRequestHandler(DatagramRequestHandler):
896
897 def handle(self):
898 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100899
900 def finish(self):
901 data = self.wfile.getvalue()
Vinay Sajip3ef12292011-05-23 23:00:42 +0100902 if data:
903 try:
904 super(DelegatingUDPRequestHandler, self).finish()
905 except socket.error:
906 if not self.server._closed:
907 raise
Vinay Sajipba980db2011-05-23 21:37:54 +0100908
Vinay Sajip3ef12292011-05-23 23:00:42 +0100909 ThreadingUDPServer.__init__(self, addr,
910 DelegatingUDPRequestHandler,
Vinay Sajipce7c9782011-05-17 07:15:53 +0100911 bind_and_activate)
912 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100913 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100914
915 def server_bind(self):
916 super(TestUDPServer, self).server_bind()
917 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100918
Vinay Sajipba980db2011-05-23 21:37:54 +0100919 def server_close(self):
920 super(TestUDPServer, self).server_close()
921 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100922
923# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100924
Vinay Sajipce7c9782011-05-17 07:15:53 +0100925@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100926class SMTPHandlerTest(BaseTest):
927 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100928 sockmap = {}
929 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
930 sockmap)
931 server.start()
932 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100933 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
934 self.assertEqual(h.toaddrs, ['you'])
935 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100936 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100937 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100938 h.handle(r)
Vinay Sajip17160fd2012-03-15 12:02:08 +0000939 self.handled.wait(5.0) # 14314: don't wait forever
Vinay Sajipa463d252011-04-30 21:52:48 +0100940 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +0000941 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +0100942 self.assertEqual(len(self.messages), 1)
943 peer, mailfrom, rcpttos, data = self.messages[0]
944 self.assertEqual(mailfrom, 'me')
945 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100946 self.assertIn('\nSubject: Log\n', data)
Vinay Sajipa463d252011-04-30 21:52:48 +0100947 self.assertTrue(data.endswith('\n\nHello'))
948 h.close()
949
950 def process_message(self, *args):
951 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100952 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100953
Christian Heimes180510d2008-03-03 19:15:45 +0000954class MemoryHandlerTest(BaseTest):
955
956 """Tests for the MemoryHandler."""
957
958 # Do not bother with a logger name group.
959 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
960
961 def setUp(self):
962 BaseTest.setUp(self)
963 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
964 self.root_hdlr)
965 self.mem_logger = logging.getLogger('mem')
966 self.mem_logger.propagate = 0
967 self.mem_logger.addHandler(self.mem_hdlr)
968
969 def tearDown(self):
970 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000971 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000972
973 def test_flush(self):
974 # The memory handler flushes to its target handler based on specific
975 # criteria (message count and message level).
976 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000977 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000978 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000979 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000980 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100981 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +0000982 lines = [
983 ('DEBUG', '1'),
984 ('INFO', '2'),
985 ('WARNING', '3'),
986 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000987 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000988 for n in (4, 14):
989 for i in range(9):
990 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000991 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000992 # This will flush because it's the 10th message since the last
993 # flush.
994 self.mem_logger.debug(self.next_message())
995 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000996 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000997
998 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000999 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001000
1001
1002class ExceptionFormatter(logging.Formatter):
1003 """A special exception formatter."""
1004 def formatException(self, ei):
1005 return "Got a [%s]" % ei[0].__name__
1006
1007
1008class ConfigFileTest(BaseTest):
1009
1010 """Reading logging config from a .ini-style config file."""
1011
1012 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1013
1014 # config0 is a standard configuration.
1015 config0 = """
1016 [loggers]
1017 keys=root
1018
1019 [handlers]
1020 keys=hand1
1021
1022 [formatters]
1023 keys=form1
1024
1025 [logger_root]
1026 level=WARNING
1027 handlers=hand1
1028
1029 [handler_hand1]
1030 class=StreamHandler
1031 level=NOTSET
1032 formatter=form1
1033 args=(sys.stdout,)
1034
1035 [formatter_form1]
1036 format=%(levelname)s ++ %(message)s
1037 datefmt=
1038 """
1039
1040 # config1 adds a little to the standard configuration.
1041 config1 = """
1042 [loggers]
1043 keys=root,parser
1044
1045 [handlers]
1046 keys=hand1
1047
1048 [formatters]
1049 keys=form1
1050
1051 [logger_root]
1052 level=WARNING
1053 handlers=
1054
1055 [logger_parser]
1056 level=DEBUG
1057 handlers=hand1
1058 propagate=1
1059 qualname=compiler.parser
1060
1061 [handler_hand1]
1062 class=StreamHandler
1063 level=NOTSET
1064 formatter=form1
1065 args=(sys.stdout,)
1066
1067 [formatter_form1]
1068 format=%(levelname)s ++ %(message)s
1069 datefmt=
1070 """
1071
Vinay Sajip3f84b072011-03-07 17:49:33 +00001072 # config1a moves the handler to the root.
1073 config1a = """
1074 [loggers]
1075 keys=root,parser
1076
1077 [handlers]
1078 keys=hand1
1079
1080 [formatters]
1081 keys=form1
1082
1083 [logger_root]
1084 level=WARNING
1085 handlers=hand1
1086
1087 [logger_parser]
1088 level=DEBUG
1089 handlers=
1090 propagate=1
1091 qualname=compiler.parser
1092
1093 [handler_hand1]
1094 class=StreamHandler
1095 level=NOTSET
1096 formatter=form1
1097 args=(sys.stdout,)
1098
1099 [formatter_form1]
1100 format=%(levelname)s ++ %(message)s
1101 datefmt=
1102 """
1103
Christian Heimes180510d2008-03-03 19:15:45 +00001104 # config2 has a subtle configuration error that should be reported
1105 config2 = config1.replace("sys.stdout", "sys.stbout")
1106
1107 # config3 has a less subtle configuration error
1108 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1109
1110 # config4 specifies a custom formatter class to be loaded
1111 config4 = """
1112 [loggers]
1113 keys=root
1114
1115 [handlers]
1116 keys=hand1
1117
1118 [formatters]
1119 keys=form1
1120
1121 [logger_root]
1122 level=NOTSET
1123 handlers=hand1
1124
1125 [handler_hand1]
1126 class=StreamHandler
1127 level=NOTSET
1128 formatter=form1
1129 args=(sys.stdout,)
1130
1131 [formatter_form1]
1132 class=""" + __name__ + """.ExceptionFormatter
1133 format=%(levelname)s:%(name)s:%(message)s
1134 datefmt=
1135 """
1136
Georg Brandl3dbca812008-07-23 16:10:53 +00001137 # config5 specifies a custom handler class to be loaded
1138 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1139
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001140 # config6 uses ', ' delimiters in the handlers and formatters sections
1141 config6 = """
1142 [loggers]
1143 keys=root,parser
1144
1145 [handlers]
1146 keys=hand1, hand2
1147
1148 [formatters]
1149 keys=form1, form2
1150
1151 [logger_root]
1152 level=WARNING
1153 handlers=
1154
1155 [logger_parser]
1156 level=DEBUG
1157 handlers=hand1
1158 propagate=1
1159 qualname=compiler.parser
1160
1161 [handler_hand1]
1162 class=StreamHandler
1163 level=NOTSET
1164 formatter=form1
1165 args=(sys.stdout,)
1166
1167 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001168 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001169 level=NOTSET
1170 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001171 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001172
1173 [formatter_form1]
1174 format=%(levelname)s ++ %(message)s
1175 datefmt=
1176
1177 [formatter_form2]
1178 format=%(message)s
1179 datefmt=
1180 """
1181
Vinay Sajip3f84b072011-03-07 17:49:33 +00001182 # config7 adds a compiler logger.
1183 config7 = """
1184 [loggers]
1185 keys=root,parser,compiler
1186
1187 [handlers]
1188 keys=hand1
1189
1190 [formatters]
1191 keys=form1
1192
1193 [logger_root]
1194 level=WARNING
1195 handlers=hand1
1196
1197 [logger_compiler]
1198 level=DEBUG
1199 handlers=
1200 propagate=1
1201 qualname=compiler
1202
1203 [logger_parser]
1204 level=DEBUG
1205 handlers=
1206 propagate=1
1207 qualname=compiler.parser
1208
1209 [handler_hand1]
1210 class=StreamHandler
1211 level=NOTSET
1212 formatter=form1
1213 args=(sys.stdout,)
1214
1215 [formatter_form1]
1216 format=%(levelname)s ++ %(message)s
1217 datefmt=
1218 """
1219
Christian Heimes180510d2008-03-03 19:15:45 +00001220 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001221 file = io.StringIO(textwrap.dedent(conf))
1222 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001223
1224 def test_config0_ok(self):
1225 # A simple config file which overrides the default settings.
1226 with captured_stdout() as output:
1227 self.apply_config(self.config0)
1228 logger = logging.getLogger()
1229 # Won't output anything
1230 logger.info(self.next_message())
1231 # Outputs a message
1232 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001233 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001234 ('ERROR', '2'),
1235 ], stream=output)
1236 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001237 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001238
Georg Brandl3dbca812008-07-23 16:10:53 +00001239 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001240 # A config file defining a sub-parser as well.
1241 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001242 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001243 logger = logging.getLogger("compiler.parser")
1244 # Both will output a message
1245 logger.info(self.next_message())
1246 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001247 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001248 ('INFO', '1'),
1249 ('ERROR', '2'),
1250 ], stream=output)
1251 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001252 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001253
1254 def test_config2_failure(self):
1255 # A simple config file which overrides the default settings.
1256 self.assertRaises(Exception, self.apply_config, self.config2)
1257
1258 def test_config3_failure(self):
1259 # A simple config file which overrides the default settings.
1260 self.assertRaises(Exception, self.apply_config, self.config3)
1261
1262 def test_config4_ok(self):
1263 # A config file specifying a custom formatter class.
1264 with captured_stdout() as output:
1265 self.apply_config(self.config4)
1266 logger = logging.getLogger()
1267 try:
1268 raise RuntimeError()
1269 except RuntimeError:
1270 logging.exception("just testing")
1271 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001272 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001273 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1274 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001275 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001276
Georg Brandl3dbca812008-07-23 16:10:53 +00001277 def test_config5_ok(self):
1278 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001279
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001280 def test_config6_ok(self):
1281 self.test_config1_ok(config=self.config6)
1282
Vinay Sajip3f84b072011-03-07 17:49:33 +00001283 def test_config7_ok(self):
1284 with captured_stdout() as output:
1285 self.apply_config(self.config1a)
1286 logger = logging.getLogger("compiler.parser")
1287 # See issue #11424. compiler-hyphenated sorts
1288 # between compiler and compiler.xyz and this
1289 # was preventing compiler.xyz from being included
1290 # in the child loggers of compiler because of an
1291 # overzealous loop termination condition.
1292 hyphenated = logging.getLogger('compiler-hyphenated')
1293 # All will output a message
1294 logger.info(self.next_message())
1295 logger.error(self.next_message())
1296 hyphenated.critical(self.next_message())
1297 self.assert_log_lines([
1298 ('INFO', '1'),
1299 ('ERROR', '2'),
1300 ('CRITICAL', '3'),
1301 ], stream=output)
1302 # Original logger output is empty.
1303 self.assert_log_lines([])
1304 with captured_stdout() as output:
1305 self.apply_config(self.config7)
1306 logger = logging.getLogger("compiler.parser")
1307 self.assertFalse(logger.disabled)
1308 # Both will output a message
1309 logger.info(self.next_message())
1310 logger.error(self.next_message())
1311 logger = logging.getLogger("compiler.lexer")
1312 # Both will output a message
1313 logger.info(self.next_message())
1314 logger.error(self.next_message())
1315 # Will not appear
1316 hyphenated.critical(self.next_message())
1317 self.assert_log_lines([
1318 ('INFO', '4'),
1319 ('ERROR', '5'),
1320 ('INFO', '6'),
1321 ('ERROR', '7'),
1322 ], stream=output)
1323 # Original logger output is empty.
1324 self.assert_log_lines([])
1325
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001326
Victor Stinner45df8202010-04-28 22:31:17 +00001327@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001328class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001329
Christian Heimes180510d2008-03-03 19:15:45 +00001330 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001331
Christian Heimes180510d2008-03-03 19:15:45 +00001332 def setUp(self):
1333 """Set up a TCP server to receive log messages, and a SocketHandler
1334 pointing to that server's address and port."""
1335 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001336 addr = ('localhost', 0)
1337 self.server = server = TestTCPServer(addr, self.handle_socket,
1338 0.01)
1339 server.start()
1340 server.ready.wait()
1341 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1342 server.port)
1343 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001344 self.root_logger.removeHandler(self.root_logger.handlers[0])
1345 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001346 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001347
Christian Heimes180510d2008-03-03 19:15:45 +00001348 def tearDown(self):
1349 """Shutdown the TCP server."""
1350 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001351 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001352 self.root_logger.removeHandler(self.sock_hdlr)
1353 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001354 finally:
1355 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001356
Vinay Sajip7367d082011-05-02 13:17:27 +01001357 def handle_socket(self, request):
1358 conn = request.connection
1359 while True:
1360 chunk = conn.recv(4)
1361 if len(chunk) < 4:
1362 break
1363 slen = struct.unpack(">L", chunk)[0]
1364 chunk = conn.recv(slen)
1365 while len(chunk) < slen:
1366 chunk = chunk + conn.recv(slen - len(chunk))
1367 obj = pickle.loads(chunk)
1368 record = logging.makeLogRecord(obj)
1369 self.log_output += record.msg + '\n'
1370 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001371
Christian Heimes180510d2008-03-03 19:15:45 +00001372 def test_output(self):
1373 # The log message sent to the SocketHandler is properly received.
1374 logger = logging.getLogger("tcp")
1375 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001376 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001377 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001378 self.handled.acquire()
1379 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001380
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001381 def test_noserver(self):
1382 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001383 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001384 #The logging call should try to connect, which should fail
1385 try:
1386 raise RuntimeError('Deliberate mistake')
1387 except RuntimeError:
1388 self.root_logger.exception('Never sent')
1389 self.root_logger.error('Never sent, either')
1390 now = time.time()
1391 self.assertTrue(self.sock_hdlr.retryTime > now)
1392 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1393 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001394
Vinay Sajip7367d082011-05-02 13:17:27 +01001395
1396@unittest.skipUnless(threading, 'Threading required for this test.')
1397class DatagramHandlerTest(BaseTest):
1398
1399 """Test for DatagramHandler."""
1400
1401 def setUp(self):
1402 """Set up a UDP server to receive log messages, and a DatagramHandler
1403 pointing to that server's address and port."""
1404 BaseTest.setUp(self)
1405 addr = ('localhost', 0)
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001406 self.server = server = TestUDPServer(addr, self.handle_datagram, 0.01)
Vinay Sajip7367d082011-05-02 13:17:27 +01001407 server.start()
1408 server.ready.wait()
1409 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1410 server.port)
1411 self.log_output = ''
1412 self.root_logger.removeHandler(self.root_logger.handlers[0])
1413 self.root_logger.addHandler(self.sock_hdlr)
1414 self.handled = threading.Event()
1415
1416 def tearDown(self):
1417 """Shutdown the UDP server."""
1418 try:
1419 self.server.stop(2.0)
1420 self.root_logger.removeHandler(self.sock_hdlr)
1421 self.sock_hdlr.close()
1422 finally:
1423 BaseTest.tearDown(self)
1424
1425 def handle_datagram(self, request):
1426 slen = struct.pack('>L', 0) # length of prefix
1427 packet = request.packet[len(slen):]
1428 obj = pickle.loads(packet)
1429 record = logging.makeLogRecord(obj)
1430 self.log_output += record.msg + '\n'
1431 self.handled.set()
1432
1433 def test_output(self):
1434 # The log message sent to the DatagramHandler is properly received.
1435 logger = logging.getLogger("udp")
1436 logger.error("spam")
1437 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001438 self.handled.clear()
1439 logger.error("eggs")
1440 self.handled.wait()
1441 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001442
1443
1444@unittest.skipUnless(threading, 'Threading required for this test.')
1445class SysLogHandlerTest(BaseTest):
1446
1447 """Test for SysLogHandler using UDP."""
1448
1449 def setUp(self):
1450 """Set up a UDP server to receive log messages, and a SysLogHandler
1451 pointing to that server's address and port."""
1452 BaseTest.setUp(self)
1453 addr = ('localhost', 0)
1454 self.server = server = TestUDPServer(addr, self.handle_datagram,
1455 0.01)
1456 server.start()
1457 server.ready.wait()
1458 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1459 server.port))
1460 self.log_output = ''
1461 self.root_logger.removeHandler(self.root_logger.handlers[0])
1462 self.root_logger.addHandler(self.sl_hdlr)
1463 self.handled = threading.Event()
1464
1465 def tearDown(self):
1466 """Shutdown the UDP server."""
1467 try:
1468 self.server.stop(2.0)
1469 self.root_logger.removeHandler(self.sl_hdlr)
1470 self.sl_hdlr.close()
1471 finally:
1472 BaseTest.tearDown(self)
1473
1474 def handle_datagram(self, request):
1475 self.log_output = request.packet
1476 self.handled.set()
1477
1478 def test_output(self):
1479 # The log message sent to the SysLogHandler is properly received.
1480 logger = logging.getLogger("slh")
1481 logger.error("sp\xe4m")
1482 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001483 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001484 self.handled.clear()
1485 self.sl_hdlr.append_nul = False
1486 logger.error("sp\xe4m")
1487 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001488 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001489 self.handled.clear()
1490 self.sl_hdlr.ident = "h\xe4m-"
1491 logger.error("sp\xe4m")
1492 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001493 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001494
1495
1496@unittest.skipUnless(threading, 'Threading required for this test.')
1497class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001498 """Test for HTTPHandler."""
1499
Vinay Sajip9ba87612011-05-21 11:32:15 +01001500 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1501MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1502zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1503aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1504AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
15050tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
15065LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1507piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
15082z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1509i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1510PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1511E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1512rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1513/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1514-----END RSA PRIVATE KEY-----
1515-----BEGIN CERTIFICATE-----
1516MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1517BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1518EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1519xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1520l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1521vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1522BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1523S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1524BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15254f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15261c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1527IRbTpvlHWPjsSvHz0ZOH
1528-----END CERTIFICATE-----"""
1529
Vinay Sajip7367d082011-05-02 13:17:27 +01001530 def setUp(self):
1531 """Set up an HTTP server to receive log messages, and a HTTPHandler
1532 pointing to that server's address and port."""
1533 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001534 self.handled = threading.Event()
1535
Vinay Sajip7367d082011-05-02 13:17:27 +01001536 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001537 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001538 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001539 if self.command == 'POST':
1540 try:
1541 rlen = int(request.headers['Content-Length'])
1542 self.post_data = request.rfile.read(rlen)
1543 except:
1544 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001545 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001546 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001547 self.handled.set()
1548
1549 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001550 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001551 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001552 root_logger = self.root_logger
1553 root_logger.removeHandler(self.root_logger.handlers[0])
1554 for secure in (False, True):
1555 addr = ('localhost', 0)
1556 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001557 try:
1558 import ssl
1559 fd, fn = tempfile.mkstemp()
1560 os.close(fd)
1561 with open(fn, 'w') as f:
1562 f.write(self.PEMFILE)
1563 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1564 sslctx.load_cert_chain(fn)
1565 os.unlink(fn)
1566 except ImportError:
1567 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001568 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001569 sslctx = None
1570 self.server = server = TestHTTPServer(addr, self.handle_request,
1571 0.01, sslctx=sslctx)
1572 server.start()
1573 server.ready.wait()
1574 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001575 secure_client = secure and sslctx
1576 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1577 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001578 self.log_data = None
1579 root_logger.addHandler(self.h_hdlr)
1580
1581 for method in ('GET', 'POST'):
1582 self.h_hdlr.method = method
1583 self.handled.clear()
1584 msg = "sp\xe4m"
1585 logger.error(msg)
1586 self.handled.wait()
1587 self.assertEqual(self.log_data.path, '/frob')
1588 self.assertEqual(self.command, method)
1589 if method == 'GET':
1590 d = parse_qs(self.log_data.query)
1591 else:
1592 d = parse_qs(self.post_data.decode('utf-8'))
1593 self.assertEqual(d['name'], ['http'])
1594 self.assertEqual(d['funcName'], ['test_output'])
1595 self.assertEqual(d['msg'], [msg])
1596
1597 self.server.stop(2.0)
1598 self.root_logger.removeHandler(self.h_hdlr)
1599 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001600
Christian Heimes180510d2008-03-03 19:15:45 +00001601class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001602
Christian Heimes180510d2008-03-03 19:15:45 +00001603 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001604
Christian Heimes180510d2008-03-03 19:15:45 +00001605 def setUp(self):
1606 """Create a dict to remember potentially destroyed objects."""
1607 BaseTest.setUp(self)
1608 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001609
Christian Heimes180510d2008-03-03 19:15:45 +00001610 def _watch_for_survival(self, *args):
1611 """Watch the given objects for survival, by creating weakrefs to
1612 them."""
1613 for obj in args:
1614 key = id(obj), repr(obj)
1615 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001616
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001617 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001618 """Assert that all objects watched for survival have survived."""
1619 # Trigger cycle breaking.
1620 gc.collect()
1621 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001622 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001623 if ref() is None:
1624 dead.append(repr_)
1625 if dead:
1626 self.fail("%d objects should have survived "
1627 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001628
Christian Heimes180510d2008-03-03 19:15:45 +00001629 def test_persistent_loggers(self):
1630 # Logger objects are persistent and retain their configuration, even
1631 # if visible references are destroyed.
1632 self.root_logger.setLevel(logging.INFO)
1633 foo = logging.getLogger("foo")
1634 self._watch_for_survival(foo)
1635 foo.setLevel(logging.DEBUG)
1636 self.root_logger.debug(self.next_message())
1637 foo.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 ])
1641 del foo
1642 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001643 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001644 # foo has retained its settings.
1645 bar = logging.getLogger("foo")
1646 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001647 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001648 ('foo', 'DEBUG', '2'),
1649 ('foo', 'DEBUG', '3'),
1650 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001651
Benjamin Petersonf91df042009-02-13 02:50:59 +00001652
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001653class EncodingTest(BaseTest):
1654 def test_encoding_plain_file(self):
1655 # In Python 2.x, a plain file object is treated as having no encoding.
1656 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001657 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1658 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001659 # the non-ascii data we write to the log.
1660 data = "foo\x80"
1661 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001662 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001663 log.addHandler(handler)
1664 try:
1665 # write non-ascii data to the log.
1666 log.warning(data)
1667 finally:
1668 log.removeHandler(handler)
1669 handler.close()
1670 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001671 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001672 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001673 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001674 finally:
1675 f.close()
1676 finally:
1677 if os.path.isfile(fn):
1678 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001679
Benjamin Petersonf91df042009-02-13 02:50:59 +00001680 def test_encoding_cyrillic_unicode(self):
1681 log = logging.getLogger("test")
1682 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1683 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1684 #Ensure it's written in a Cyrillic encoding
1685 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001686 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001687 stream = io.BytesIO()
1688 writer = writer_class(stream, 'strict')
1689 handler = logging.StreamHandler(writer)
1690 log.addHandler(handler)
1691 try:
1692 log.warning(message)
1693 finally:
1694 log.removeHandler(handler)
1695 handler.close()
1696 # check we wrote exactly those bytes, ignoring trailing \n etc
1697 s = stream.getvalue()
1698 #Compare against what the data should be when encoded in CP-1251
1699 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1700
1701
Georg Brandlf9734072008-12-07 15:30:06 +00001702class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001703
Georg Brandlf9734072008-12-07 15:30:06 +00001704 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001705 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001706 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001707 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001708 warnings.filterwarnings("always", category=UserWarning)
1709 stream = io.StringIO()
1710 h = logging.StreamHandler(stream)
1711 logger = logging.getLogger("py.warnings")
1712 logger.addHandler(h)
1713 warnings.warn("I'm warning you...")
1714 logger.removeHandler(h)
1715 s = stream.getvalue()
1716 h.close()
1717 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001718
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001719 #See if an explicit file uses the original implementation
1720 a_file = io.StringIO()
1721 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1722 a_file, "Dummy line")
1723 s = a_file.getvalue()
1724 a_file.close()
1725 self.assertEqual(s,
1726 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1727
1728 def test_warnings_no_handlers(self):
1729 with warnings.catch_warnings():
1730 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001731 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001732
1733 # confirm our assumption: no loggers are set
1734 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001735 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001736
1737 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001738 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001739 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001740
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001741
1742def formatFunc(format, datefmt=None):
1743 return logging.Formatter(format, datefmt)
1744
1745def handlerFunc():
1746 return logging.StreamHandler()
1747
1748class CustomHandler(logging.StreamHandler):
1749 pass
1750
1751class ConfigDictTest(BaseTest):
1752
1753 """Reading logging config from a dictionary."""
1754
1755 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1756
1757 # config0 is a standard configuration.
1758 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001759 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001760 'formatters': {
1761 'form1' : {
1762 'format' : '%(levelname)s ++ %(message)s',
1763 },
1764 },
1765 'handlers' : {
1766 'hand1' : {
1767 'class' : 'logging.StreamHandler',
1768 'formatter' : 'form1',
1769 'level' : 'NOTSET',
1770 'stream' : 'ext://sys.stdout',
1771 },
1772 },
1773 'root' : {
1774 'level' : 'WARNING',
1775 'handlers' : ['hand1'],
1776 },
1777 }
1778
1779 # config1 adds a little to the standard configuration.
1780 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001781 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001782 'formatters': {
1783 'form1' : {
1784 'format' : '%(levelname)s ++ %(message)s',
1785 },
1786 },
1787 'handlers' : {
1788 'hand1' : {
1789 'class' : 'logging.StreamHandler',
1790 'formatter' : 'form1',
1791 'level' : 'NOTSET',
1792 'stream' : 'ext://sys.stdout',
1793 },
1794 },
1795 'loggers' : {
1796 'compiler.parser' : {
1797 'level' : 'DEBUG',
1798 'handlers' : ['hand1'],
1799 },
1800 },
1801 'root' : {
1802 'level' : 'WARNING',
1803 },
1804 }
1805
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001806 # config1a moves the handler to the root. Used with config8a
1807 config1a = {
1808 'version': 1,
1809 'formatters': {
1810 'form1' : {
1811 'format' : '%(levelname)s ++ %(message)s',
1812 },
1813 },
1814 'handlers' : {
1815 'hand1' : {
1816 'class' : 'logging.StreamHandler',
1817 'formatter' : 'form1',
1818 'level' : 'NOTSET',
1819 'stream' : 'ext://sys.stdout',
1820 },
1821 },
1822 'loggers' : {
1823 'compiler.parser' : {
1824 'level' : 'DEBUG',
1825 },
1826 },
1827 'root' : {
1828 'level' : 'WARNING',
1829 'handlers' : ['hand1'],
1830 },
1831 }
1832
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001833 # config2 has a subtle configuration error that should be reported
1834 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001835 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001836 'formatters': {
1837 'form1' : {
1838 'format' : '%(levelname)s ++ %(message)s',
1839 },
1840 },
1841 'handlers' : {
1842 'hand1' : {
1843 'class' : 'logging.StreamHandler',
1844 'formatter' : 'form1',
1845 'level' : 'NOTSET',
1846 'stream' : 'ext://sys.stdbout',
1847 },
1848 },
1849 'loggers' : {
1850 'compiler.parser' : {
1851 'level' : 'DEBUG',
1852 'handlers' : ['hand1'],
1853 },
1854 },
1855 'root' : {
1856 'level' : 'WARNING',
1857 },
1858 }
1859
1860 #As config1 but with a misspelt level on a handler
1861 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001862 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001863 'formatters': {
1864 'form1' : {
1865 'format' : '%(levelname)s ++ %(message)s',
1866 },
1867 },
1868 'handlers' : {
1869 'hand1' : {
1870 'class' : 'logging.StreamHandler',
1871 'formatter' : 'form1',
1872 'level' : 'NTOSET',
1873 'stream' : 'ext://sys.stdout',
1874 },
1875 },
1876 'loggers' : {
1877 'compiler.parser' : {
1878 'level' : 'DEBUG',
1879 'handlers' : ['hand1'],
1880 },
1881 },
1882 'root' : {
1883 'level' : 'WARNING',
1884 },
1885 }
1886
1887
1888 #As config1 but with a misspelt level on a logger
1889 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001890 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001891 'formatters': {
1892 'form1' : {
1893 'format' : '%(levelname)s ++ %(message)s',
1894 },
1895 },
1896 'handlers' : {
1897 'hand1' : {
1898 'class' : 'logging.StreamHandler',
1899 'formatter' : 'form1',
1900 'level' : 'NOTSET',
1901 'stream' : 'ext://sys.stdout',
1902 },
1903 },
1904 'loggers' : {
1905 'compiler.parser' : {
1906 'level' : 'DEBUG',
1907 'handlers' : ['hand1'],
1908 },
1909 },
1910 'root' : {
1911 'level' : 'WRANING',
1912 },
1913 }
1914
1915 # config3 has a less subtle configuration error
1916 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001917 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001918 'formatters': {
1919 'form1' : {
1920 'format' : '%(levelname)s ++ %(message)s',
1921 },
1922 },
1923 'handlers' : {
1924 'hand1' : {
1925 'class' : 'logging.StreamHandler',
1926 'formatter' : 'misspelled_name',
1927 'level' : 'NOTSET',
1928 'stream' : 'ext://sys.stdout',
1929 },
1930 },
1931 'loggers' : {
1932 'compiler.parser' : {
1933 'level' : 'DEBUG',
1934 'handlers' : ['hand1'],
1935 },
1936 },
1937 'root' : {
1938 'level' : 'WARNING',
1939 },
1940 }
1941
1942 # config4 specifies a custom formatter class to be loaded
1943 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001944 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001945 'formatters': {
1946 'form1' : {
1947 '()' : __name__ + '.ExceptionFormatter',
1948 'format' : '%(levelname)s:%(name)s:%(message)s',
1949 },
1950 },
1951 'handlers' : {
1952 'hand1' : {
1953 'class' : 'logging.StreamHandler',
1954 'formatter' : 'form1',
1955 'level' : 'NOTSET',
1956 'stream' : 'ext://sys.stdout',
1957 },
1958 },
1959 'root' : {
1960 'level' : 'NOTSET',
1961 'handlers' : ['hand1'],
1962 },
1963 }
1964
1965 # As config4 but using an actual callable rather than a string
1966 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001967 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001968 'formatters': {
1969 'form1' : {
1970 '()' : ExceptionFormatter,
1971 'format' : '%(levelname)s:%(name)s:%(message)s',
1972 },
1973 'form2' : {
1974 '()' : __name__ + '.formatFunc',
1975 'format' : '%(levelname)s:%(name)s:%(message)s',
1976 },
1977 'form3' : {
1978 '()' : formatFunc,
1979 'format' : '%(levelname)s:%(name)s:%(message)s',
1980 },
1981 },
1982 'handlers' : {
1983 'hand1' : {
1984 'class' : 'logging.StreamHandler',
1985 'formatter' : 'form1',
1986 'level' : 'NOTSET',
1987 'stream' : 'ext://sys.stdout',
1988 },
1989 'hand2' : {
1990 '()' : handlerFunc,
1991 },
1992 },
1993 'root' : {
1994 'level' : 'NOTSET',
1995 'handlers' : ['hand1'],
1996 },
1997 }
1998
1999 # config5 specifies a custom handler class to be loaded
2000 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002001 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002002 'formatters': {
2003 'form1' : {
2004 'format' : '%(levelname)s ++ %(message)s',
2005 },
2006 },
2007 'handlers' : {
2008 'hand1' : {
2009 'class' : __name__ + '.CustomHandler',
2010 'formatter' : 'form1',
2011 'level' : 'NOTSET',
2012 'stream' : 'ext://sys.stdout',
2013 },
2014 },
2015 'loggers' : {
2016 'compiler.parser' : {
2017 'level' : 'DEBUG',
2018 'handlers' : ['hand1'],
2019 },
2020 },
2021 'root' : {
2022 'level' : 'WARNING',
2023 },
2024 }
2025
2026 # config6 specifies a custom handler class to be loaded
2027 # but has bad arguments
2028 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002029 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002030 'formatters': {
2031 'form1' : {
2032 'format' : '%(levelname)s ++ %(message)s',
2033 },
2034 },
2035 'handlers' : {
2036 'hand1' : {
2037 'class' : __name__ + '.CustomHandler',
2038 'formatter' : 'form1',
2039 'level' : 'NOTSET',
2040 'stream' : 'ext://sys.stdout',
2041 '9' : 'invalid parameter name',
2042 },
2043 },
2044 'loggers' : {
2045 'compiler.parser' : {
2046 'level' : 'DEBUG',
2047 'handlers' : ['hand1'],
2048 },
2049 },
2050 'root' : {
2051 'level' : 'WARNING',
2052 },
2053 }
2054
2055 #config 7 does not define compiler.parser but defines compiler.lexer
2056 #so compiler.parser should be disabled after applying it
2057 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002058 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002059 'formatters': {
2060 'form1' : {
2061 'format' : '%(levelname)s ++ %(message)s',
2062 },
2063 },
2064 'handlers' : {
2065 'hand1' : {
2066 'class' : 'logging.StreamHandler',
2067 'formatter' : 'form1',
2068 'level' : 'NOTSET',
2069 'stream' : 'ext://sys.stdout',
2070 },
2071 },
2072 'loggers' : {
2073 'compiler.lexer' : {
2074 'level' : 'DEBUG',
2075 'handlers' : ['hand1'],
2076 },
2077 },
2078 'root' : {
2079 'level' : 'WARNING',
2080 },
2081 }
2082
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002083 # config8 defines both compiler and compiler.lexer
2084 # so compiler.parser should not be disabled (since
2085 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002086 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002087 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002088 'disable_existing_loggers' : False,
2089 'formatters': {
2090 'form1' : {
2091 'format' : '%(levelname)s ++ %(message)s',
2092 },
2093 },
2094 'handlers' : {
2095 'hand1' : {
2096 'class' : 'logging.StreamHandler',
2097 'formatter' : 'form1',
2098 'level' : 'NOTSET',
2099 'stream' : 'ext://sys.stdout',
2100 },
2101 },
2102 'loggers' : {
2103 'compiler' : {
2104 'level' : 'DEBUG',
2105 'handlers' : ['hand1'],
2106 },
2107 'compiler.lexer' : {
2108 },
2109 },
2110 'root' : {
2111 'level' : 'WARNING',
2112 },
2113 }
2114
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002115 # config8a disables existing loggers
2116 config8a = {
2117 'version': 1,
2118 'disable_existing_loggers' : True,
2119 'formatters': {
2120 'form1' : {
2121 'format' : '%(levelname)s ++ %(message)s',
2122 },
2123 },
2124 'handlers' : {
2125 'hand1' : {
2126 'class' : 'logging.StreamHandler',
2127 'formatter' : 'form1',
2128 'level' : 'NOTSET',
2129 'stream' : 'ext://sys.stdout',
2130 },
2131 },
2132 'loggers' : {
2133 'compiler' : {
2134 'level' : 'DEBUG',
2135 'handlers' : ['hand1'],
2136 },
2137 'compiler.lexer' : {
2138 },
2139 },
2140 'root' : {
2141 'level' : 'WARNING',
2142 },
2143 }
2144
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002145 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002146 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002147 'formatters': {
2148 'form1' : {
2149 'format' : '%(levelname)s ++ %(message)s',
2150 },
2151 },
2152 'handlers' : {
2153 'hand1' : {
2154 'class' : 'logging.StreamHandler',
2155 'formatter' : 'form1',
2156 'level' : 'WARNING',
2157 'stream' : 'ext://sys.stdout',
2158 },
2159 },
2160 'loggers' : {
2161 'compiler.parser' : {
2162 'level' : 'WARNING',
2163 'handlers' : ['hand1'],
2164 },
2165 },
2166 'root' : {
2167 'level' : 'NOTSET',
2168 },
2169 }
2170
2171 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002172 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002173 'incremental' : True,
2174 'handlers' : {
2175 'hand1' : {
2176 'level' : 'WARNING',
2177 },
2178 },
2179 'loggers' : {
2180 'compiler.parser' : {
2181 'level' : 'INFO',
2182 },
2183 },
2184 }
2185
2186 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002187 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002188 'incremental' : True,
2189 'handlers' : {
2190 'hand1' : {
2191 'level' : 'INFO',
2192 },
2193 },
2194 'loggers' : {
2195 'compiler.parser' : {
2196 'level' : 'INFO',
2197 },
2198 },
2199 }
2200
2201 #As config1 but with a filter added
2202 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002203 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002204 'formatters': {
2205 'form1' : {
2206 'format' : '%(levelname)s ++ %(message)s',
2207 },
2208 },
2209 'filters' : {
2210 'filt1' : {
2211 'name' : 'compiler.parser',
2212 },
2213 },
2214 'handlers' : {
2215 'hand1' : {
2216 'class' : 'logging.StreamHandler',
2217 'formatter' : 'form1',
2218 'level' : 'NOTSET',
2219 'stream' : 'ext://sys.stdout',
2220 'filters' : ['filt1'],
2221 },
2222 },
2223 'loggers' : {
2224 'compiler.parser' : {
2225 'level' : 'DEBUG',
2226 'filters' : ['filt1'],
2227 },
2228 },
2229 'root' : {
2230 'level' : 'WARNING',
2231 'handlers' : ['hand1'],
2232 },
2233 }
2234
2235 #As config1 but using cfg:// references
2236 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002237 'version': 1,
2238 'true_formatters': {
2239 'form1' : {
2240 'format' : '%(levelname)s ++ %(message)s',
2241 },
2242 },
2243 'handler_configs': {
2244 'hand1' : {
2245 'class' : 'logging.StreamHandler',
2246 'formatter' : 'form1',
2247 'level' : 'NOTSET',
2248 'stream' : 'ext://sys.stdout',
2249 },
2250 },
2251 'formatters' : 'cfg://true_formatters',
2252 'handlers' : {
2253 'hand1' : 'cfg://handler_configs[hand1]',
2254 },
2255 'loggers' : {
2256 'compiler.parser' : {
2257 'level' : 'DEBUG',
2258 'handlers' : ['hand1'],
2259 },
2260 },
2261 'root' : {
2262 'level' : 'WARNING',
2263 },
2264 }
2265
2266 #As config11 but missing the version key
2267 config12 = {
2268 'true_formatters': {
2269 'form1' : {
2270 'format' : '%(levelname)s ++ %(message)s',
2271 },
2272 },
2273 'handler_configs': {
2274 'hand1' : {
2275 'class' : 'logging.StreamHandler',
2276 'formatter' : 'form1',
2277 'level' : 'NOTSET',
2278 'stream' : 'ext://sys.stdout',
2279 },
2280 },
2281 'formatters' : 'cfg://true_formatters',
2282 'handlers' : {
2283 'hand1' : 'cfg://handler_configs[hand1]',
2284 },
2285 'loggers' : {
2286 'compiler.parser' : {
2287 'level' : 'DEBUG',
2288 'handlers' : ['hand1'],
2289 },
2290 },
2291 'root' : {
2292 'level' : 'WARNING',
2293 },
2294 }
2295
2296 #As config11 but using an unsupported version
2297 config13 = {
2298 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002299 'true_formatters': {
2300 'form1' : {
2301 'format' : '%(levelname)s ++ %(message)s',
2302 },
2303 },
2304 'handler_configs': {
2305 'hand1' : {
2306 'class' : 'logging.StreamHandler',
2307 'formatter' : 'form1',
2308 'level' : 'NOTSET',
2309 'stream' : 'ext://sys.stdout',
2310 },
2311 },
2312 'formatters' : 'cfg://true_formatters',
2313 'handlers' : {
2314 'hand1' : 'cfg://handler_configs[hand1]',
2315 },
2316 'loggers' : {
2317 'compiler.parser' : {
2318 'level' : 'DEBUG',
2319 'handlers' : ['hand1'],
2320 },
2321 },
2322 'root' : {
2323 'level' : 'WARNING',
2324 },
2325 }
2326
2327 def apply_config(self, conf):
2328 logging.config.dictConfig(conf)
2329
2330 def test_config0_ok(self):
2331 # A simple config which overrides the default settings.
2332 with captured_stdout() as output:
2333 self.apply_config(self.config0)
2334 logger = logging.getLogger()
2335 # Won't output anything
2336 logger.info(self.next_message())
2337 # Outputs a message
2338 logger.error(self.next_message())
2339 self.assert_log_lines([
2340 ('ERROR', '2'),
2341 ], stream=output)
2342 # Original logger output is empty.
2343 self.assert_log_lines([])
2344
2345 def test_config1_ok(self, config=config1):
2346 # A config defining a sub-parser as well.
2347 with captured_stdout() as output:
2348 self.apply_config(config)
2349 logger = logging.getLogger("compiler.parser")
2350 # Both will output a message
2351 logger.info(self.next_message())
2352 logger.error(self.next_message())
2353 self.assert_log_lines([
2354 ('INFO', '1'),
2355 ('ERROR', '2'),
2356 ], stream=output)
2357 # Original logger output is empty.
2358 self.assert_log_lines([])
2359
2360 def test_config2_failure(self):
2361 # A simple config which overrides the default settings.
2362 self.assertRaises(Exception, self.apply_config, self.config2)
2363
2364 def test_config2a_failure(self):
2365 # A simple config which overrides the default settings.
2366 self.assertRaises(Exception, self.apply_config, self.config2a)
2367
2368 def test_config2b_failure(self):
2369 # A simple config which overrides the default settings.
2370 self.assertRaises(Exception, self.apply_config, self.config2b)
2371
2372 def test_config3_failure(self):
2373 # A simple config which overrides the default settings.
2374 self.assertRaises(Exception, self.apply_config, self.config3)
2375
2376 def test_config4_ok(self):
2377 # A config specifying a custom formatter class.
2378 with captured_stdout() as output:
2379 self.apply_config(self.config4)
2380 #logger = logging.getLogger()
2381 try:
2382 raise RuntimeError()
2383 except RuntimeError:
2384 logging.exception("just testing")
2385 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002386 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002387 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2388 # Original logger output is empty
2389 self.assert_log_lines([])
2390
2391 def test_config4a_ok(self):
2392 # A config specifying a custom formatter class.
2393 with captured_stdout() as output:
2394 self.apply_config(self.config4a)
2395 #logger = logging.getLogger()
2396 try:
2397 raise RuntimeError()
2398 except RuntimeError:
2399 logging.exception("just testing")
2400 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002401 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002402 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2403 # Original logger output is empty
2404 self.assert_log_lines([])
2405
2406 def test_config5_ok(self):
2407 self.test_config1_ok(config=self.config5)
2408
2409 def test_config6_failure(self):
2410 self.assertRaises(Exception, self.apply_config, self.config6)
2411
2412 def test_config7_ok(self):
2413 with captured_stdout() as output:
2414 self.apply_config(self.config1)
2415 logger = logging.getLogger("compiler.parser")
2416 # Both will output a message
2417 logger.info(self.next_message())
2418 logger.error(self.next_message())
2419 self.assert_log_lines([
2420 ('INFO', '1'),
2421 ('ERROR', '2'),
2422 ], stream=output)
2423 # Original logger output is empty.
2424 self.assert_log_lines([])
2425 with captured_stdout() as output:
2426 self.apply_config(self.config7)
2427 logger = logging.getLogger("compiler.parser")
2428 self.assertTrue(logger.disabled)
2429 logger = logging.getLogger("compiler.lexer")
2430 # Both will output a message
2431 logger.info(self.next_message())
2432 logger.error(self.next_message())
2433 self.assert_log_lines([
2434 ('INFO', '3'),
2435 ('ERROR', '4'),
2436 ], stream=output)
2437 # Original logger output is empty.
2438 self.assert_log_lines([])
2439
2440 #Same as test_config_7_ok but don't disable old loggers.
2441 def test_config_8_ok(self):
2442 with captured_stdout() as output:
2443 self.apply_config(self.config1)
2444 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002445 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002446 logger.info(self.next_message())
2447 logger.error(self.next_message())
2448 self.assert_log_lines([
2449 ('INFO', '1'),
2450 ('ERROR', '2'),
2451 ], stream=output)
2452 # Original logger output is empty.
2453 self.assert_log_lines([])
2454 with captured_stdout() as output:
2455 self.apply_config(self.config8)
2456 logger = logging.getLogger("compiler.parser")
2457 self.assertFalse(logger.disabled)
2458 # Both will output a message
2459 logger.info(self.next_message())
2460 logger.error(self.next_message())
2461 logger = logging.getLogger("compiler.lexer")
2462 # Both will output a message
2463 logger.info(self.next_message())
2464 logger.error(self.next_message())
2465 self.assert_log_lines([
2466 ('INFO', '3'),
2467 ('ERROR', '4'),
2468 ('INFO', '5'),
2469 ('ERROR', '6'),
2470 ], stream=output)
2471 # Original logger output is empty.
2472 self.assert_log_lines([])
2473
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002474 def test_config_8a_ok(self):
2475 with captured_stdout() as output:
2476 self.apply_config(self.config1a)
2477 logger = logging.getLogger("compiler.parser")
2478 # See issue #11424. compiler-hyphenated sorts
2479 # between compiler and compiler.xyz and this
2480 # was preventing compiler.xyz from being included
2481 # in the child loggers of compiler because of an
2482 # overzealous loop termination condition.
2483 hyphenated = logging.getLogger('compiler-hyphenated')
2484 # All will output a message
2485 logger.info(self.next_message())
2486 logger.error(self.next_message())
2487 hyphenated.critical(self.next_message())
2488 self.assert_log_lines([
2489 ('INFO', '1'),
2490 ('ERROR', '2'),
2491 ('CRITICAL', '3'),
2492 ], stream=output)
2493 # Original logger output is empty.
2494 self.assert_log_lines([])
2495 with captured_stdout() as output:
2496 self.apply_config(self.config8a)
2497 logger = logging.getLogger("compiler.parser")
2498 self.assertFalse(logger.disabled)
2499 # Both will output a message
2500 logger.info(self.next_message())
2501 logger.error(self.next_message())
2502 logger = logging.getLogger("compiler.lexer")
2503 # Both will output a message
2504 logger.info(self.next_message())
2505 logger.error(self.next_message())
2506 # Will not appear
2507 hyphenated.critical(self.next_message())
2508 self.assert_log_lines([
2509 ('INFO', '4'),
2510 ('ERROR', '5'),
2511 ('INFO', '6'),
2512 ('ERROR', '7'),
2513 ], stream=output)
2514 # Original logger output is empty.
2515 self.assert_log_lines([])
2516
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002517 def test_config_9_ok(self):
2518 with captured_stdout() as output:
2519 self.apply_config(self.config9)
2520 logger = logging.getLogger("compiler.parser")
2521 #Nothing will be output since both handler and logger are set to WARNING
2522 logger.info(self.next_message())
2523 self.assert_log_lines([], stream=output)
2524 self.apply_config(self.config9a)
2525 #Nothing will be output since both handler is still set to WARNING
2526 logger.info(self.next_message())
2527 self.assert_log_lines([], stream=output)
2528 self.apply_config(self.config9b)
2529 #Message should now be output
2530 logger.info(self.next_message())
2531 self.assert_log_lines([
2532 ('INFO', '3'),
2533 ], stream=output)
2534
2535 def test_config_10_ok(self):
2536 with captured_stdout() as output:
2537 self.apply_config(self.config10)
2538 logger = logging.getLogger("compiler.parser")
2539 logger.warning(self.next_message())
2540 logger = logging.getLogger('compiler')
2541 #Not output, because filtered
2542 logger.warning(self.next_message())
2543 logger = logging.getLogger('compiler.lexer')
2544 #Not output, because filtered
2545 logger.warning(self.next_message())
2546 logger = logging.getLogger("compiler.parser.codegen")
2547 #Output, as not filtered
2548 logger.error(self.next_message())
2549 self.assert_log_lines([
2550 ('WARNING', '1'),
2551 ('ERROR', '4'),
2552 ], stream=output)
2553
2554 def test_config11_ok(self):
2555 self.test_config1_ok(self.config11)
2556
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002557 def test_config12_failure(self):
2558 self.assertRaises(Exception, self.apply_config, self.config12)
2559
2560 def test_config13_failure(self):
2561 self.assertRaises(Exception, self.apply_config, self.config13)
2562
Victor Stinner45df8202010-04-28 22:31:17 +00002563 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002564 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002565 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002566 # Ask for a randomly assigned port (by using port 0)
2567 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002568 t.start()
2569 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002570 # Now get the port allocated
2571 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002572 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002573 try:
2574 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2575 sock.settimeout(2.0)
2576 sock.connect(('localhost', port))
2577
2578 slen = struct.pack('>L', len(text))
2579 s = slen + text
2580 sentsofar = 0
2581 left = len(s)
2582 while left > 0:
2583 sent = sock.send(s[sentsofar:])
2584 sentsofar += sent
2585 left -= sent
2586 sock.close()
2587 finally:
2588 t.ready.wait(2.0)
2589 logging.config.stopListening()
2590 t.join(2.0)
2591
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002592 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002593 def test_listen_config_10_ok(self):
2594 with captured_stdout() as output:
2595 self.setup_via_listener(json.dumps(self.config10))
2596 logger = logging.getLogger("compiler.parser")
2597 logger.warning(self.next_message())
2598 logger = logging.getLogger('compiler')
2599 #Not output, because filtered
2600 logger.warning(self.next_message())
2601 logger = logging.getLogger('compiler.lexer')
2602 #Not output, because filtered
2603 logger.warning(self.next_message())
2604 logger = logging.getLogger("compiler.parser.codegen")
2605 #Output, as not filtered
2606 logger.error(self.next_message())
2607 self.assert_log_lines([
2608 ('WARNING', '1'),
2609 ('ERROR', '4'),
2610 ], stream=output)
2611
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002612 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002613 def test_listen_config_1_ok(self):
2614 with captured_stdout() as output:
2615 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2616 logger = logging.getLogger("compiler.parser")
2617 # Both will output a message
2618 logger.info(self.next_message())
2619 logger.error(self.next_message())
2620 self.assert_log_lines([
2621 ('INFO', '1'),
2622 ('ERROR', '2'),
2623 ], stream=output)
2624 # Original logger output is empty.
2625 self.assert_log_lines([])
2626
Vinay Sajip373baef2011-04-26 20:05:24 +01002627 def test_baseconfig(self):
2628 d = {
2629 'atuple': (1, 2, 3),
2630 'alist': ['a', 'b', 'c'],
2631 'adict': {'d': 'e', 'f': 3 },
2632 'nest1': ('g', ('h', 'i'), 'j'),
2633 'nest2': ['k', ['l', 'm'], 'n'],
2634 'nest3': ['o', 'cfg://alist', 'p'],
2635 }
2636 bc = logging.config.BaseConfigurator(d)
2637 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2638 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2639 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2640 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2641 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2642 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2643 v = bc.convert('cfg://nest3')
2644 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2645 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2646 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2647 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002648
2649class ManagerTest(BaseTest):
2650 def test_manager_loggerclass(self):
2651 logged = []
2652
2653 class MyLogger(logging.Logger):
2654 def _log(self, level, msg, args, exc_info=None, extra=None):
2655 logged.append(msg)
2656
2657 man = logging.Manager(None)
2658 self.assertRaises(TypeError, man.setLoggerClass, int)
2659 man.setLoggerClass(MyLogger)
2660 logger = man.getLogger('test')
2661 logger.warning('should appear in logged')
2662 logging.warning('should not appear in logged')
2663
2664 self.assertEqual(logged, ['should appear in logged'])
2665
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002666 def test_set_log_record_factory(self):
2667 man = logging.Manager(None)
2668 expected = object()
2669 man.setLogRecordFactory(expected)
2670 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002671
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002672class ChildLoggerTest(BaseTest):
2673 def test_child_loggers(self):
2674 r = logging.getLogger()
2675 l1 = logging.getLogger('abc')
2676 l2 = logging.getLogger('def.ghi')
2677 c1 = r.getChild('xyz')
2678 c2 = r.getChild('uvw.xyz')
2679 self.assertTrue(c1 is logging.getLogger('xyz'))
2680 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2681 c1 = l1.getChild('def')
2682 c2 = c1.getChild('ghi')
2683 c3 = l1.getChild('def.ghi')
2684 self.assertTrue(c1 is logging.getLogger('abc.def'))
2685 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2686 self.assertTrue(c2 is c3)
2687
2688
Vinay Sajip6fac8172010-10-19 20:44:14 +00002689class DerivedLogRecord(logging.LogRecord):
2690 pass
2691
Vinay Sajip61561522010-12-03 11:50:38 +00002692class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002693
2694 def setUp(self):
2695 class CheckingFilter(logging.Filter):
2696 def __init__(self, cls):
2697 self.cls = cls
2698
2699 def filter(self, record):
2700 t = type(record)
2701 if t is not self.cls:
2702 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2703 self.cls)
2704 raise TypeError(msg)
2705 return True
2706
2707 BaseTest.setUp(self)
2708 self.filter = CheckingFilter(DerivedLogRecord)
2709 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002710 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002711
2712 def tearDown(self):
2713 self.root_logger.removeFilter(self.filter)
2714 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002715 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002716
2717 def test_logrecord_class(self):
2718 self.assertRaises(TypeError, self.root_logger.warning,
2719 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002720 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002721 self.root_logger.error(self.next_message())
2722 self.assert_log_lines([
2723 ('root', 'ERROR', '2'),
2724 ])
2725
2726
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002727class QueueHandlerTest(BaseTest):
2728 # Do not bother with a logger name group.
2729 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2730
2731 def setUp(self):
2732 BaseTest.setUp(self)
2733 self.queue = queue.Queue(-1)
2734 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2735 self.que_logger = logging.getLogger('que')
2736 self.que_logger.propagate = False
2737 self.que_logger.setLevel(logging.WARNING)
2738 self.que_logger.addHandler(self.que_hdlr)
2739
2740 def tearDown(self):
2741 self.que_hdlr.close()
2742 BaseTest.tearDown(self)
2743
2744 def test_queue_handler(self):
2745 self.que_logger.debug(self.next_message())
2746 self.assertRaises(queue.Empty, self.queue.get_nowait)
2747 self.que_logger.info(self.next_message())
2748 self.assertRaises(queue.Empty, self.queue.get_nowait)
2749 msg = self.next_message()
2750 self.que_logger.warning(msg)
2751 data = self.queue.get_nowait()
2752 self.assertTrue(isinstance(data, logging.LogRecord))
2753 self.assertEqual(data.name, self.que_logger.name)
2754 self.assertEqual((data.msg, data.args), (msg, None))
2755
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002756 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2757 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002758 def test_queue_listener(self):
2759 handler = TestHandler(Matcher())
2760 listener = logging.handlers.QueueListener(self.queue, handler)
2761 listener.start()
2762 try:
2763 self.que_logger.warning(self.next_message())
2764 self.que_logger.error(self.next_message())
2765 self.que_logger.critical(self.next_message())
2766 finally:
2767 listener.stop()
2768 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2769 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2770 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2771
Vinay Sajip37eb3382011-04-26 20:26:41 +01002772ZERO = datetime.timedelta(0)
2773
2774class UTC(datetime.tzinfo):
2775 def utcoffset(self, dt):
2776 return ZERO
2777
2778 dst = utcoffset
2779
2780 def tzname(self, dt):
2781 return 'UTC'
2782
2783utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002784
Vinay Sajipa39c5712010-10-25 13:57:39 +00002785class FormatterTest(unittest.TestCase):
2786 def setUp(self):
2787 self.common = {
2788 'name': 'formatter.test',
2789 'level': logging.DEBUG,
2790 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2791 'lineno': 42,
2792 'exc_info': None,
2793 'func': None,
2794 'msg': 'Message with %d %s',
2795 'args': (2, 'placeholders'),
2796 }
2797 self.variants = {
2798 }
2799
2800 def get_record(self, name=None):
2801 result = dict(self.common)
2802 if name is not None:
2803 result.update(self.variants[name])
2804 return logging.makeLogRecord(result)
2805
2806 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002807 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002808 r = self.get_record()
2809 f = logging.Formatter('${%(message)s}')
2810 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2811 f = logging.Formatter('%(random)s')
2812 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002813 self.assertFalse(f.usesTime())
2814 f = logging.Formatter('%(asctime)s')
2815 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002816 f = logging.Formatter('%(asctime)-15s')
2817 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002818 f = logging.Formatter('asctime')
2819 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002820
2821 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002822 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002823 r = self.get_record()
2824 f = logging.Formatter('$%{message}%$', style='{')
2825 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2826 f = logging.Formatter('{random}', style='{')
2827 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002828 self.assertFalse(f.usesTime())
2829 f = logging.Formatter('{asctime}', style='{')
2830 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002831 f = logging.Formatter('{asctime!s:15}', style='{')
2832 self.assertTrue(f.usesTime())
2833 f = logging.Formatter('{asctime:15}', style='{')
2834 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002835 f = logging.Formatter('asctime', style='{')
2836 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002837
2838 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002839 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002840 r = self.get_record()
2841 f = logging.Formatter('$message', style='$')
2842 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2843 f = logging.Formatter('$$%${message}%$$', style='$')
2844 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2845 f = logging.Formatter('${random}', style='$')
2846 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002847 self.assertFalse(f.usesTime())
2848 f = logging.Formatter('${asctime}', style='$')
2849 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002850 f = logging.Formatter('${asctime', style='$')
2851 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002852 f = logging.Formatter('$asctime', style='$')
2853 self.assertTrue(f.usesTime())
2854 f = logging.Formatter('asctime', style='$')
2855 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002856
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002857 def test_invalid_style(self):
2858 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2859
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002860 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002861 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002862 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002863 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002864 r.msecs = 123
2865 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002866 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002867 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2868 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002869 f.format(r)
2870 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2871
2872class TestBufferingFormatter(logging.BufferingFormatter):
2873 def formatHeader(self, records):
2874 return '[(%d)' % len(records)
2875
2876 def formatFooter(self, records):
2877 return '(%d)]' % len(records)
2878
2879class BufferingFormatterTest(unittest.TestCase):
2880 def setUp(self):
2881 self.records = [
2882 logging.makeLogRecord({'msg': 'one'}),
2883 logging.makeLogRecord({'msg': 'two'}),
2884 ]
2885
2886 def test_default(self):
2887 f = logging.BufferingFormatter()
2888 self.assertEqual('', f.format([]))
2889 self.assertEqual('onetwo', f.format(self.records))
2890
2891 def test_custom(self):
2892 f = TestBufferingFormatter()
2893 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2894 lf = logging.Formatter('<%(message)s>')
2895 f = TestBufferingFormatter(lf)
2896 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002897
2898class ExceptionTest(BaseTest):
2899 def test_formatting(self):
2900 r = self.root_logger
2901 h = RecordingHandler()
2902 r.addHandler(h)
2903 try:
2904 raise RuntimeError('deliberate mistake')
2905 except:
2906 logging.exception('failed', stack_info=True)
2907 r.removeHandler(h)
2908 h.close()
2909 r = h.records[0]
2910 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2911 'call last):\n'))
2912 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2913 'deliberate mistake'))
2914 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2915 'call last):\n'))
2916 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2917 'stack_info=True)'))
2918
2919
Vinay Sajip5a27d402010-12-10 11:42:57 +00002920class LastResortTest(BaseTest):
2921 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002922 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002923 root = self.root_logger
2924 root.removeHandler(self.root_hdlr)
2925 old_stderr = sys.stderr
2926 old_lastresort = logging.lastResort
2927 old_raise_exceptions = logging.raiseExceptions
2928 try:
2929 sys.stderr = sio = io.StringIO()
Vinay Sajip8188f582011-07-25 19:58:13 +01002930 root.debug('This should not appear')
2931 self.assertEqual(sio.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00002932 root.warning('This is your final chance!')
2933 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2934 #No handlers and no last resort, so 'No handlers' message
2935 logging.lastResort = None
2936 sys.stderr = sio = io.StringIO()
2937 root.warning('This is your final chance!')
2938 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2939 # 'No handlers' message only printed once
2940 sys.stderr = sio = io.StringIO()
2941 root.warning('This is your final chance!')
2942 self.assertEqual(sio.getvalue(), '')
2943 root.manager.emittedNoHandlerWarning = False
2944 #If raiseExceptions is False, no message is printed
2945 logging.raiseExceptions = False
2946 sys.stderr = sio = io.StringIO()
2947 root.warning('This is your final chance!')
2948 self.assertEqual(sio.getvalue(), '')
2949 finally:
2950 sys.stderr = old_stderr
2951 root.addHandler(self.root_hdlr)
2952 logging.lastResort = old_lastresort
2953 logging.raiseExceptions = old_raise_exceptions
2954
2955
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002956class FakeHandler:
2957
2958 def __init__(self, identifier, called):
2959 for method in ('acquire', 'flush', 'close', 'release'):
2960 setattr(self, method, self.record_call(identifier, method, called))
2961
2962 def record_call(self, identifier, method_name, called):
2963 def inner():
2964 called.append('{} - {}'.format(identifier, method_name))
2965 return inner
2966
2967
2968class RecordingHandler(logging.NullHandler):
2969
2970 def __init__(self, *args, **kwargs):
2971 super(RecordingHandler, self).__init__(*args, **kwargs)
2972 self.records = []
2973
2974 def handle(self, record):
2975 """Keep track of all the emitted records."""
2976 self.records.append(record)
2977
2978
2979class ShutdownTest(BaseTest):
2980
Vinay Sajip5e66b162011-04-20 15:41:14 +01002981 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002982
2983 def setUp(self):
2984 super(ShutdownTest, self).setUp()
2985 self.called = []
2986
2987 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002988 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002989
2990 def raise_error(self, error):
2991 def inner():
2992 raise error()
2993 return inner
2994
2995 def test_no_failure(self):
2996 # create some fake handlers
2997 handler0 = FakeHandler(0, self.called)
2998 handler1 = FakeHandler(1, self.called)
2999 handler2 = FakeHandler(2, self.called)
3000
3001 # create live weakref to those handlers
3002 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
3003
3004 logging.shutdown(handlerList=list(handlers))
3005
3006 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
3007 '1 - acquire', '1 - flush', '1 - close', '1 - release',
3008 '0 - acquire', '0 - flush', '0 - close', '0 - release']
3009 self.assertEqual(expected, self.called)
3010
3011 def _test_with_failure_in_method(self, method, error):
3012 handler = FakeHandler(0, self.called)
3013 setattr(handler, method, self.raise_error(error))
3014 handlers = [logging.weakref.ref(handler)]
3015
3016 logging.shutdown(handlerList=list(handlers))
3017
3018 self.assertEqual('0 - release', self.called[-1])
3019
3020 def test_with_ioerror_in_acquire(self):
3021 self._test_with_failure_in_method('acquire', IOError)
3022
3023 def test_with_ioerror_in_flush(self):
3024 self._test_with_failure_in_method('flush', IOError)
3025
3026 def test_with_ioerror_in_close(self):
3027 self._test_with_failure_in_method('close', IOError)
3028
3029 def test_with_valueerror_in_acquire(self):
3030 self._test_with_failure_in_method('acquire', ValueError)
3031
3032 def test_with_valueerror_in_flush(self):
3033 self._test_with_failure_in_method('flush', ValueError)
3034
3035 def test_with_valueerror_in_close(self):
3036 self._test_with_failure_in_method('close', ValueError)
3037
3038 def test_with_other_error_in_acquire_without_raise(self):
3039 logging.raiseExceptions = False
3040 self._test_with_failure_in_method('acquire', IndexError)
3041
3042 def test_with_other_error_in_flush_without_raise(self):
3043 logging.raiseExceptions = False
3044 self._test_with_failure_in_method('flush', IndexError)
3045
3046 def test_with_other_error_in_close_without_raise(self):
3047 logging.raiseExceptions = False
3048 self._test_with_failure_in_method('close', IndexError)
3049
3050 def test_with_other_error_in_acquire_with_raise(self):
3051 logging.raiseExceptions = True
3052 self.assertRaises(IndexError, self._test_with_failure_in_method,
3053 'acquire', IndexError)
3054
3055 def test_with_other_error_in_flush_with_raise(self):
3056 logging.raiseExceptions = True
3057 self.assertRaises(IndexError, self._test_with_failure_in_method,
3058 'flush', IndexError)
3059
3060 def test_with_other_error_in_close_with_raise(self):
3061 logging.raiseExceptions = True
3062 self.assertRaises(IndexError, self._test_with_failure_in_method,
3063 'close', IndexError)
3064
3065
3066class ModuleLevelMiscTest(BaseTest):
3067
Vinay Sajip5e66b162011-04-20 15:41:14 +01003068 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003069
3070 def test_disable(self):
3071 old_disable = logging.root.manager.disable
3072 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003073 self.assertEqual(old_disable, 0)
3074 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003075
3076 logging.disable(83)
3077 self.assertEqual(logging.root.manager.disable, 83)
3078
3079 def _test_log(self, method, level=None):
3080 called = []
3081 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003082 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003083
3084 recording = RecordingHandler()
3085 logging.root.addHandler(recording)
3086
3087 log_method = getattr(logging, method)
3088 if level is not None:
3089 log_method(level, "test me: %r", recording)
3090 else:
3091 log_method("test me: %r", recording)
3092
3093 self.assertEqual(len(recording.records), 1)
3094 record = recording.records[0]
3095 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3096
3097 expected_level = level if level is not None else getattr(logging, method.upper())
3098 self.assertEqual(record.levelno, expected_level)
3099
3100 # basicConfig was not called!
3101 self.assertEqual(called, [])
3102
3103 def test_log(self):
3104 self._test_log('log', logging.ERROR)
3105
3106 def test_debug(self):
3107 self._test_log('debug')
3108
3109 def test_info(self):
3110 self._test_log('info')
3111
3112 def test_warning(self):
3113 self._test_log('warning')
3114
3115 def test_error(self):
3116 self._test_log('error')
3117
3118 def test_critical(self):
3119 self._test_log('critical')
3120
3121 def test_set_logger_class(self):
3122 self.assertRaises(TypeError, logging.setLoggerClass, object)
3123
3124 class MyLogger(logging.Logger):
3125 pass
3126
3127 logging.setLoggerClass(MyLogger)
3128 self.assertEqual(logging.getLoggerClass(), MyLogger)
3129
3130 logging.setLoggerClass(logging.Logger)
3131 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3132
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003133class LogRecordTest(BaseTest):
3134 def test_str_rep(self):
3135 r = logging.makeLogRecord({})
3136 s = str(r)
3137 self.assertTrue(s.startswith('<LogRecord: '))
3138 self.assertTrue(s.endswith('>'))
3139
3140 def test_dict_arg(self):
3141 h = RecordingHandler()
3142 r = logging.getLogger()
3143 r.addHandler(h)
3144 d = {'less' : 'more' }
3145 logging.warning('less is %(less)s', d)
3146 self.assertIs(h.records[0].args, d)
3147 self.assertEqual(h.records[0].message, 'less is more')
3148 r.removeHandler(h)
3149 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003150
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003151 def test_multiprocessing(self):
3152 r = logging.makeLogRecord({})
3153 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003154 try:
3155 import multiprocessing as mp
3156 r = logging.makeLogRecord({})
3157 self.assertEqual(r.processName, mp.current_process().name)
3158 except ImportError:
3159 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003160
3161 def test_optional(self):
3162 r = logging.makeLogRecord({})
3163 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003164 if threading:
3165 NOT_NONE(r.thread)
3166 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003167 NOT_NONE(r.process)
3168 NOT_NONE(r.processName)
3169 log_threads = logging.logThreads
3170 log_processes = logging.logProcesses
3171 log_multiprocessing = logging.logMultiprocessing
3172 try:
3173 logging.logThreads = False
3174 logging.logProcesses = False
3175 logging.logMultiprocessing = False
3176 r = logging.makeLogRecord({})
3177 NONE = self.assertIsNone
3178 NONE(r.thread)
3179 NONE(r.threadName)
3180 NONE(r.process)
3181 NONE(r.processName)
3182 finally:
3183 logging.logThreads = log_threads
3184 logging.logProcesses = log_processes
3185 logging.logMultiprocessing = log_multiprocessing
3186
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003187class BasicConfigTest(unittest.TestCase):
3188
Vinay Sajip95bf5042011-04-20 11:50:56 +01003189 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003190
3191 def setUp(self):
3192 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003193 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003194 self.saved_handlers = logging._handlers.copy()
3195 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003196 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003197 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003198 logging.root.handlers = []
3199
3200 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003201 for h in logging.root.handlers[:]:
3202 logging.root.removeHandler(h)
3203 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003204 super(BasicConfigTest, self).tearDown()
3205
Vinay Sajip3def7e02011-04-20 10:58:06 +01003206 def cleanup(self):
3207 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003208 logging._handlers.clear()
3209 logging._handlers.update(self.saved_handlers)
3210 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003211 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003212
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003213 def test_no_kwargs(self):
3214 logging.basicConfig()
3215
3216 # handler defaults to a StreamHandler to sys.stderr
3217 self.assertEqual(len(logging.root.handlers), 1)
3218 handler = logging.root.handlers[0]
3219 self.assertIsInstance(handler, logging.StreamHandler)
3220 self.assertEqual(handler.stream, sys.stderr)
3221
3222 formatter = handler.formatter
3223 # format defaults to logging.BASIC_FORMAT
3224 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3225 # datefmt defaults to None
3226 self.assertIsNone(formatter.datefmt)
3227 # style defaults to %
3228 self.assertIsInstance(formatter._style, logging.PercentStyle)
3229
Florent Xiclunae41f0de2011-11-11 19:39:25 +01003230 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003231 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003232
3233 def test_filename(self):
3234 logging.basicConfig(filename='test.log')
3235
3236 self.assertEqual(len(logging.root.handlers), 1)
3237 handler = logging.root.handlers[0]
3238 self.assertIsInstance(handler, logging.FileHandler)
3239
3240 expected = logging.FileHandler('test.log', 'a')
3241 self.addCleanup(expected.close)
3242 self.assertEqual(handler.stream.mode, expected.stream.mode)
3243 self.assertEqual(handler.stream.name, expected.stream.name)
3244
3245 def test_filemode(self):
3246 logging.basicConfig(filename='test.log', filemode='wb')
3247
3248 handler = logging.root.handlers[0]
3249 expected = logging.FileHandler('test.log', 'wb')
3250 self.addCleanup(expected.close)
3251 self.assertEqual(handler.stream.mode, expected.stream.mode)
3252
3253 def test_stream(self):
3254 stream = io.StringIO()
3255 self.addCleanup(stream.close)
3256 logging.basicConfig(stream=stream)
3257
3258 self.assertEqual(len(logging.root.handlers), 1)
3259 handler = logging.root.handlers[0]
3260 self.assertIsInstance(handler, logging.StreamHandler)
3261 self.assertEqual(handler.stream, stream)
3262
3263 def test_format(self):
3264 logging.basicConfig(format='foo')
3265
3266 formatter = logging.root.handlers[0].formatter
3267 self.assertEqual(formatter._style._fmt, 'foo')
3268
3269 def test_datefmt(self):
3270 logging.basicConfig(datefmt='bar')
3271
3272 formatter = logging.root.handlers[0].formatter
3273 self.assertEqual(formatter.datefmt, 'bar')
3274
3275 def test_style(self):
3276 logging.basicConfig(style='$')
3277
3278 formatter = logging.root.handlers[0].formatter
3279 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3280
3281 def test_level(self):
3282 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003283 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003284
3285 logging.basicConfig(level=57)
3286 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003287 # Test that second call has no effect
3288 logging.basicConfig(level=58)
3289 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003290
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003291 def test_incompatible(self):
3292 assertRaises = self.assertRaises
3293 handlers = [logging.StreamHandler()]
3294 stream = sys.stderr
3295 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3296 stream=stream)
3297 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3298 handlers=handlers)
3299 assertRaises(ValueError, logging.basicConfig, stream=stream,
3300 handlers=handlers)
3301
3302 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003303 handlers = [
3304 logging.StreamHandler(),
3305 logging.StreamHandler(sys.stdout),
3306 logging.StreamHandler(),
3307 ]
3308 f = logging.Formatter()
3309 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003310 logging.basicConfig(handlers=handlers)
3311 self.assertIs(handlers[0], logging.root.handlers[0])
3312 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003313 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003314 self.assertIsNotNone(handlers[0].formatter)
3315 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003316 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003317 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3318
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003319 def _test_log(self, method, level=None):
3320 # logging.root has no handlers so basicConfig should be called
3321 called = []
3322
3323 old_basic_config = logging.basicConfig
3324 def my_basic_config(*a, **kw):
3325 old_basic_config()
3326 old_level = logging.root.level
3327 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003328 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003329 called.append((a, kw))
3330
3331 patch(self, logging, 'basicConfig', my_basic_config)
3332
3333 log_method = getattr(logging, method)
3334 if level is not None:
3335 log_method(level, "test me")
3336 else:
3337 log_method("test me")
3338
3339 # basicConfig was called with no arguments
3340 self.assertEqual(called, [((), {})])
3341
3342 def test_log(self):
3343 self._test_log('log', logging.WARNING)
3344
3345 def test_debug(self):
3346 self._test_log('debug')
3347
3348 def test_info(self):
3349 self._test_log('info')
3350
3351 def test_warning(self):
3352 self._test_log('warning')
3353
3354 def test_error(self):
3355 self._test_log('error')
3356
3357 def test_critical(self):
3358 self._test_log('critical')
3359
3360
3361class LoggerAdapterTest(unittest.TestCase):
3362
3363 def setUp(self):
3364 super(LoggerAdapterTest, self).setUp()
3365 old_handler_list = logging._handlerList[:]
3366
3367 self.recording = RecordingHandler()
3368 self.logger = logging.root
3369 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003370 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003371 self.addCleanup(self.recording.close)
3372
3373 def cleanup():
3374 logging._handlerList[:] = old_handler_list
3375
3376 self.addCleanup(cleanup)
3377 self.addCleanup(logging.shutdown)
3378 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3379
3380 def test_exception(self):
3381 msg = 'testing exception: %r'
3382 exc = None
3383 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003384 1 / 0
3385 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003386 exc = e
3387 self.adapter.exception(msg, self.recording)
3388
3389 self.assertEqual(len(self.recording.records), 1)
3390 record = self.recording.records[0]
3391 self.assertEqual(record.levelno, logging.ERROR)
3392 self.assertEqual(record.msg, msg)
3393 self.assertEqual(record.args, (self.recording,))
3394 self.assertEqual(record.exc_info,
3395 (exc.__class__, exc, exc.__traceback__))
3396
3397 def test_critical(self):
3398 msg = 'critical test! %r'
3399 self.adapter.critical(msg, self.recording)
3400
3401 self.assertEqual(len(self.recording.records), 1)
3402 record = self.recording.records[0]
3403 self.assertEqual(record.levelno, logging.CRITICAL)
3404 self.assertEqual(record.msg, msg)
3405 self.assertEqual(record.args, (self.recording,))
3406
3407 def test_is_enabled_for(self):
3408 old_disable = self.adapter.logger.manager.disable
3409 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003410 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3411 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003412 self.assertFalse(self.adapter.isEnabledFor(32))
3413
3414 def test_has_handlers(self):
3415 self.assertTrue(self.adapter.hasHandlers())
3416
3417 for handler in self.logger.handlers:
3418 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003419
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003420 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003421 self.assertFalse(self.adapter.hasHandlers())
3422
3423
3424class LoggerTest(BaseTest):
3425
3426 def setUp(self):
3427 super(LoggerTest, self).setUp()
3428 self.recording = RecordingHandler()
3429 self.logger = logging.Logger(name='blah')
3430 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003431 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003432 self.addCleanup(self.recording.close)
3433 self.addCleanup(logging.shutdown)
3434
3435 def test_set_invalid_level(self):
3436 self.assertRaises(TypeError, self.logger.setLevel, object())
3437
3438 def test_exception(self):
3439 msg = 'testing exception: %r'
3440 exc = None
3441 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003442 1 / 0
3443 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003444 exc = e
3445 self.logger.exception(msg, self.recording)
3446
3447 self.assertEqual(len(self.recording.records), 1)
3448 record = self.recording.records[0]
3449 self.assertEqual(record.levelno, logging.ERROR)
3450 self.assertEqual(record.msg, msg)
3451 self.assertEqual(record.args, (self.recording,))
3452 self.assertEqual(record.exc_info,
3453 (exc.__class__, exc, exc.__traceback__))
3454
3455 def test_log_invalid_level_with_raise(self):
3456 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003457 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003458
3459 logging.raiseExceptions = True
3460 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3461
3462 def test_log_invalid_level_no_raise(self):
3463 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003464 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003465
3466 logging.raiseExceptions = False
3467 self.logger.log('10', 'test message') # no exception happens
3468
3469 def test_find_caller_with_stack_info(self):
3470 called = []
3471 patch(self, logging.traceback, 'print_stack',
3472 lambda f, file: called.append(file.getvalue()))
3473
3474 self.logger.findCaller(stack_info=True)
3475
3476 self.assertEqual(len(called), 1)
3477 self.assertEqual('Stack (most recent call last):\n', called[0])
3478
3479 def test_make_record_with_extra_overwrite(self):
3480 name = 'my record'
3481 level = 13
3482 fn = lno = msg = args = exc_info = func = sinfo = None
3483 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3484 exc_info, func, sinfo)
3485
3486 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3487 extra = {key: 'some value'}
3488 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3489 fn, lno, msg, args, exc_info,
3490 extra=extra, sinfo=sinfo)
3491
3492 def test_make_record_with_extra_no_overwrite(self):
3493 name = 'my record'
3494 level = 13
3495 fn = lno = msg = args = exc_info = func = sinfo = None
3496 extra = {'valid_key': 'some value'}
3497 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3498 exc_info, extra=extra, sinfo=sinfo)
3499 self.assertIn('valid_key', result.__dict__)
3500
3501 def test_has_handlers(self):
3502 self.assertTrue(self.logger.hasHandlers())
3503
3504 for handler in self.logger.handlers:
3505 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003506 self.assertFalse(self.logger.hasHandlers())
3507
3508 def test_has_handlers_no_propagate(self):
3509 child_logger = logging.getLogger('blah.child')
3510 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003511 self.assertFalse(child_logger.hasHandlers())
3512
3513 def test_is_enabled_for(self):
3514 old_disable = self.logger.manager.disable
3515 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003516 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003517 self.assertFalse(self.logger.isEnabledFor(22))
3518
Florent Xicluna5252f9f2011-11-07 19:43:05 +01003519 def test_root_logger_aliases(self):
3520 root = logging.getLogger()
3521 self.assertIs(root, logging.root)
3522 self.assertIs(root, logging.getLogger(None))
3523 self.assertIs(root, logging.getLogger(''))
3524 self.assertIs(root, logging.getLogger('foo').root)
3525 self.assertIs(root, logging.getLogger('foo.bar').root)
3526 self.assertIs(root, logging.getLogger('foo').parent)
3527
3528 self.assertIsNot(root, logging.getLogger('\0'))
3529 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
3530
3531 def test_invalid_names(self):
3532 self.assertRaises(TypeError, logging.getLogger, any)
3533 self.assertRaises(TypeError, logging.getLogger, b'foo')
3534
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003535
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003536class BaseFileTest(BaseTest):
3537 "Base class for handler tests that write log files"
3538
3539 def setUp(self):
3540 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003541 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3542 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003543 self.rmfiles = []
3544
3545 def tearDown(self):
3546 for fn in self.rmfiles:
3547 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003548 if os.path.exists(self.fn):
3549 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003550 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003551
3552 def assertLogFile(self, filename):
3553 "Assert a log file is there and register it for deletion"
3554 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003555 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003556 self.rmfiles.append(filename)
3557
3558
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003559class FileHandlerTest(BaseFileTest):
3560 def test_delay(self):
3561 os.unlink(self.fn)
3562 fh = logging.FileHandler(self.fn, delay=True)
3563 self.assertIsNone(fh.stream)
3564 self.assertFalse(os.path.exists(self.fn))
3565 fh.handle(logging.makeLogRecord({}))
3566 self.assertIsNotNone(fh.stream)
3567 self.assertTrue(os.path.exists(self.fn))
3568 fh.close()
3569
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003570class RotatingFileHandlerTest(BaseFileTest):
3571 def next_rec(self):
3572 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3573 self.next_message(), None, None, None)
3574
3575 def test_should_not_rollover(self):
3576 # If maxbytes is zero rollover never occurs
3577 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3578 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003579 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003580
3581 def test_should_rollover(self):
3582 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3583 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003584 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003585
3586 def test_file_created(self):
3587 # checks that the file is created and assumes it was created
3588 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003589 rh = logging.handlers.RotatingFileHandler(self.fn)
3590 rh.emit(self.next_rec())
3591 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003592 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003593
3594 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00003595 def namer(name):
3596 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003597 rh = logging.handlers.RotatingFileHandler(
3598 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003599 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003600 rh.emit(self.next_rec())
3601 self.assertLogFile(self.fn)
3602 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003603 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003604 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003605 self.assertLogFile(namer(self.fn + ".2"))
3606 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
3607 rh.close()
3608
Eric V. Smith851cad72012-03-11 22:46:04 -07003609 @requires_zlib
Vinay Sajip23b94d02012-01-04 12:02:26 +00003610 def test_rotator(self):
3611 def namer(name):
3612 return name + ".gz"
3613
3614 def rotator(source, dest):
3615 with open(source, "rb") as sf:
3616 data = sf.read()
3617 compressed = zlib.compress(data, 9)
3618 with open(dest, "wb") as df:
3619 df.write(compressed)
3620 os.remove(source)
3621
3622 rh = logging.handlers.RotatingFileHandler(
3623 self.fn, backupCount=2, maxBytes=1)
3624 rh.rotator = rotator
3625 rh.namer = namer
3626 m1 = self.next_rec()
3627 rh.emit(m1)
3628 self.assertLogFile(self.fn)
3629 m2 = self.next_rec()
3630 rh.emit(m2)
3631 fn = namer(self.fn + ".1")
3632 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003633 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00003634 with open(fn, "rb") as f:
3635 compressed = f.read()
3636 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003637 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003638 rh.emit(self.next_rec())
3639 fn = namer(self.fn + ".2")
3640 self.assertLogFile(fn)
3641 with open(fn, "rb") as f:
3642 compressed = f.read()
3643 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003644 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003645 rh.emit(self.next_rec())
3646 fn = namer(self.fn + ".2")
3647 with open(fn, "rb") as f:
3648 compressed = f.read()
3649 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003650 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003651 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00003652 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003653
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003654class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003655 # other test methods added below
3656 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003657 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3658 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00003659 fmt = logging.Formatter('%(asctime)s %(message)s')
3660 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003661 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003662 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003663 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003664 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00003665 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003666 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01003667 fh.close()
3668 # At this point, we should have a recent rotated file which we
3669 # can test for the existence of. However, in practice, on some
3670 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003671 # in time to go to look for the log file. So, we go back a fair
3672 # bit, and stop as soon as we see a rotated file. In theory this
3673 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003674 found = False
3675 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003676 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003677 for secs in range(GO_BACK):
3678 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003679 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3680 found = os.path.exists(fn)
3681 if found:
3682 self.rmfiles.append(fn)
3683 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003684 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3685 if not found:
3686 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003687 dn, fn = os.path.split(self.fn)
3688 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003689 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3690 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00003691 for f in files:
3692 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00003693 path = os.path.join(dn, f)
3694 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00003695 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01003696 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003697
Vinay Sajip0372e102011-05-05 12:59:14 +01003698 def test_invalid(self):
3699 assertRaises = self.assertRaises
3700 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003701 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003702 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003703 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003704 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003705 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003706
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003707def secs(**kw):
3708 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3709
3710for when, exp in (('S', 1),
3711 ('M', 60),
3712 ('H', 60 * 60),
3713 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003714 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003715 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003716 ('W0', secs(days=4, hours=24)),
3717 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003718 def test_compute_rollover(self, when=when, exp=exp):
3719 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003720 self.fn, when=when, interval=1, backupCount=0, utc=True)
3721 currentTime = 0.0
3722 actual = rh.computeRollover(currentTime)
3723 if exp != actual:
3724 # Failures occur on some systems for MIDNIGHT and W0.
3725 # Print detailed calculation for MIDNIGHT so we can try to see
3726 # what's going on
3727 import time
3728 if when == 'MIDNIGHT':
3729 try:
3730 if rh.utc:
3731 t = time.gmtime(currentTime)
3732 else:
3733 t = time.localtime(currentTime)
3734 currentHour = t[3]
3735 currentMinute = t[4]
3736 currentSecond = t[5]
3737 # r is the number of seconds left between now and midnight
3738 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3739 currentMinute) * 60 +
3740 currentSecond)
3741 result = currentTime + r
3742 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3743 print('currentHour: %s' % currentHour, file=sys.stderr)
3744 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3745 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3746 print('r: %s' % r, file=sys.stderr)
3747 print('result: %s' % result, file=sys.stderr)
3748 except Exception:
3749 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3750 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003751 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003752 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3753
Vinay Sajip60ccd822011-05-09 17:32:09 +01003754
3755@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3756class NTEventLogHandlerTest(BaseTest):
3757 def test_basic(self):
3758 logtype = 'Application'
3759 elh = win32evtlog.OpenEventLog(None, logtype)
3760 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3761 h = logging.handlers.NTEventLogHandler('test_logging')
3762 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3763 h.handle(r)
3764 h.close()
3765 # Now see if the event is recorded
3766 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3767 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3768 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3769 found = False
3770 GO_BACK = 100
3771 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3772 for e in events:
3773 if e.SourceName != 'test_logging':
3774 continue
3775 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3776 if msg != 'Test Log Message\r\n':
3777 continue
3778 found = True
3779 break
3780 msg = 'Record not found in event log, went back %d records' % GO_BACK
3781 self.assertTrue(found, msg=msg)
3782
Christian Heimes180510d2008-03-03 19:15:45 +00003783# Set the locale to the platform-dependent default. I have no idea
3784# why the test does this, but in any case we save the current locale
3785# first and restore it at the end.
3786@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003787def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003788 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003789 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003790 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3791 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3792 ManagerTest, FormatterTest, BufferingFormatterTest,
3793 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3794 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3795 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3796 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003797 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003798 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003799 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003800 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003801
Christian Heimes180510d2008-03-03 19:15:45 +00003802if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003803 test_main()