blob: be84be682f7173bcae3846e9c24ef842fe5d7b26 [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 Sajip7367d082011-05-02 13:17:27 +0100939 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100940 server.stop()
941 self.assertEqual(len(self.messages), 1)
942 peer, mailfrom, rcpttos, data = self.messages[0]
943 self.assertEqual(mailfrom, 'me')
944 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100945 self.assertIn('\nSubject: Log\n', data)
Vinay Sajipa463d252011-04-30 21:52:48 +0100946 self.assertTrue(data.endswith('\n\nHello'))
947 h.close()
948
949 def process_message(self, *args):
950 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100951 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100952
Christian Heimes180510d2008-03-03 19:15:45 +0000953class MemoryHandlerTest(BaseTest):
954
955 """Tests for the MemoryHandler."""
956
957 # Do not bother with a logger name group.
958 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
959
960 def setUp(self):
961 BaseTest.setUp(self)
962 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
963 self.root_hdlr)
964 self.mem_logger = logging.getLogger('mem')
965 self.mem_logger.propagate = 0
966 self.mem_logger.addHandler(self.mem_hdlr)
967
968 def tearDown(self):
969 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000970 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000971
972 def test_flush(self):
973 # The memory handler flushes to its target handler based on specific
974 # criteria (message count and message level).
975 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000976 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000977 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000978 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000979 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100980 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +0000981 lines = [
982 ('DEBUG', '1'),
983 ('INFO', '2'),
984 ('WARNING', '3'),
985 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000986 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000987 for n in (4, 14):
988 for i in range(9):
989 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000990 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000991 # This will flush because it's the 10th message since the last
992 # flush.
993 self.mem_logger.debug(self.next_message())
994 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000995 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000996
997 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000998 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000999
1000
1001class ExceptionFormatter(logging.Formatter):
1002 """A special exception formatter."""
1003 def formatException(self, ei):
1004 return "Got a [%s]" % ei[0].__name__
1005
1006
1007class ConfigFileTest(BaseTest):
1008
1009 """Reading logging config from a .ini-style config file."""
1010
1011 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1012
1013 # config0 is a standard configuration.
1014 config0 = """
1015 [loggers]
1016 keys=root
1017
1018 [handlers]
1019 keys=hand1
1020
1021 [formatters]
1022 keys=form1
1023
1024 [logger_root]
1025 level=WARNING
1026 handlers=hand1
1027
1028 [handler_hand1]
1029 class=StreamHandler
1030 level=NOTSET
1031 formatter=form1
1032 args=(sys.stdout,)
1033
1034 [formatter_form1]
1035 format=%(levelname)s ++ %(message)s
1036 datefmt=
1037 """
1038
1039 # config1 adds a little to the standard configuration.
1040 config1 = """
1041 [loggers]
1042 keys=root,parser
1043
1044 [handlers]
1045 keys=hand1
1046
1047 [formatters]
1048 keys=form1
1049
1050 [logger_root]
1051 level=WARNING
1052 handlers=
1053
1054 [logger_parser]
1055 level=DEBUG
1056 handlers=hand1
1057 propagate=1
1058 qualname=compiler.parser
1059
1060 [handler_hand1]
1061 class=StreamHandler
1062 level=NOTSET
1063 formatter=form1
1064 args=(sys.stdout,)
1065
1066 [formatter_form1]
1067 format=%(levelname)s ++ %(message)s
1068 datefmt=
1069 """
1070
Vinay Sajip3f84b072011-03-07 17:49:33 +00001071 # config1a moves the handler to the root.
1072 config1a = """
1073 [loggers]
1074 keys=root,parser
1075
1076 [handlers]
1077 keys=hand1
1078
1079 [formatters]
1080 keys=form1
1081
1082 [logger_root]
1083 level=WARNING
1084 handlers=hand1
1085
1086 [logger_parser]
1087 level=DEBUG
1088 handlers=
1089 propagate=1
1090 qualname=compiler.parser
1091
1092 [handler_hand1]
1093 class=StreamHandler
1094 level=NOTSET
1095 formatter=form1
1096 args=(sys.stdout,)
1097
1098 [formatter_form1]
1099 format=%(levelname)s ++ %(message)s
1100 datefmt=
1101 """
1102
Christian Heimes180510d2008-03-03 19:15:45 +00001103 # config2 has a subtle configuration error that should be reported
1104 config2 = config1.replace("sys.stdout", "sys.stbout")
1105
1106 # config3 has a less subtle configuration error
1107 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1108
1109 # config4 specifies a custom formatter class to be loaded
1110 config4 = """
1111 [loggers]
1112 keys=root
1113
1114 [handlers]
1115 keys=hand1
1116
1117 [formatters]
1118 keys=form1
1119
1120 [logger_root]
1121 level=NOTSET
1122 handlers=hand1
1123
1124 [handler_hand1]
1125 class=StreamHandler
1126 level=NOTSET
1127 formatter=form1
1128 args=(sys.stdout,)
1129
1130 [formatter_form1]
1131 class=""" + __name__ + """.ExceptionFormatter
1132 format=%(levelname)s:%(name)s:%(message)s
1133 datefmt=
1134 """
1135
Georg Brandl3dbca812008-07-23 16:10:53 +00001136 # config5 specifies a custom handler class to be loaded
1137 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1138
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001139 # config6 uses ', ' delimiters in the handlers and formatters sections
1140 config6 = """
1141 [loggers]
1142 keys=root,parser
1143
1144 [handlers]
1145 keys=hand1, hand2
1146
1147 [formatters]
1148 keys=form1, form2
1149
1150 [logger_root]
1151 level=WARNING
1152 handlers=
1153
1154 [logger_parser]
1155 level=DEBUG
1156 handlers=hand1
1157 propagate=1
1158 qualname=compiler.parser
1159
1160 [handler_hand1]
1161 class=StreamHandler
1162 level=NOTSET
1163 formatter=form1
1164 args=(sys.stdout,)
1165
1166 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001167 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001168 level=NOTSET
1169 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001170 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001171
1172 [formatter_form1]
1173 format=%(levelname)s ++ %(message)s
1174 datefmt=
1175
1176 [formatter_form2]
1177 format=%(message)s
1178 datefmt=
1179 """
1180
Vinay Sajip3f84b072011-03-07 17:49:33 +00001181 # config7 adds a compiler logger.
1182 config7 = """
1183 [loggers]
1184 keys=root,parser,compiler
1185
1186 [handlers]
1187 keys=hand1
1188
1189 [formatters]
1190 keys=form1
1191
1192 [logger_root]
1193 level=WARNING
1194 handlers=hand1
1195
1196 [logger_compiler]
1197 level=DEBUG
1198 handlers=
1199 propagate=1
1200 qualname=compiler
1201
1202 [logger_parser]
1203 level=DEBUG
1204 handlers=
1205 propagate=1
1206 qualname=compiler.parser
1207
1208 [handler_hand1]
1209 class=StreamHandler
1210 level=NOTSET
1211 formatter=form1
1212 args=(sys.stdout,)
1213
1214 [formatter_form1]
1215 format=%(levelname)s ++ %(message)s
1216 datefmt=
1217 """
1218
Christian Heimes180510d2008-03-03 19:15:45 +00001219 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001220 file = io.StringIO(textwrap.dedent(conf))
1221 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001222
1223 def test_config0_ok(self):
1224 # A simple config file which overrides the default settings.
1225 with captured_stdout() as output:
1226 self.apply_config(self.config0)
1227 logger = logging.getLogger()
1228 # Won't output anything
1229 logger.info(self.next_message())
1230 # Outputs a message
1231 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001232 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001233 ('ERROR', '2'),
1234 ], stream=output)
1235 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001236 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001237
Georg Brandl3dbca812008-07-23 16:10:53 +00001238 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001239 # A config file defining a sub-parser as well.
1240 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001241 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001242 logger = logging.getLogger("compiler.parser")
1243 # Both will output a message
1244 logger.info(self.next_message())
1245 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001246 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001247 ('INFO', '1'),
1248 ('ERROR', '2'),
1249 ], stream=output)
1250 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001251 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001252
1253 def test_config2_failure(self):
1254 # A simple config file which overrides the default settings.
1255 self.assertRaises(Exception, self.apply_config, self.config2)
1256
1257 def test_config3_failure(self):
1258 # A simple config file which overrides the default settings.
1259 self.assertRaises(Exception, self.apply_config, self.config3)
1260
1261 def test_config4_ok(self):
1262 # A config file specifying a custom formatter class.
1263 with captured_stdout() as output:
1264 self.apply_config(self.config4)
1265 logger = logging.getLogger()
1266 try:
1267 raise RuntimeError()
1268 except RuntimeError:
1269 logging.exception("just testing")
1270 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001271 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001272 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1273 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001274 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001275
Georg Brandl3dbca812008-07-23 16:10:53 +00001276 def test_config5_ok(self):
1277 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001278
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001279 def test_config6_ok(self):
1280 self.test_config1_ok(config=self.config6)
1281
Vinay Sajip3f84b072011-03-07 17:49:33 +00001282 def test_config7_ok(self):
1283 with captured_stdout() as output:
1284 self.apply_config(self.config1a)
1285 logger = logging.getLogger("compiler.parser")
1286 # See issue #11424. compiler-hyphenated sorts
1287 # between compiler and compiler.xyz and this
1288 # was preventing compiler.xyz from being included
1289 # in the child loggers of compiler because of an
1290 # overzealous loop termination condition.
1291 hyphenated = logging.getLogger('compiler-hyphenated')
1292 # All will output a message
1293 logger.info(self.next_message())
1294 logger.error(self.next_message())
1295 hyphenated.critical(self.next_message())
1296 self.assert_log_lines([
1297 ('INFO', '1'),
1298 ('ERROR', '2'),
1299 ('CRITICAL', '3'),
1300 ], stream=output)
1301 # Original logger output is empty.
1302 self.assert_log_lines([])
1303 with captured_stdout() as output:
1304 self.apply_config(self.config7)
1305 logger = logging.getLogger("compiler.parser")
1306 self.assertFalse(logger.disabled)
1307 # Both will output a message
1308 logger.info(self.next_message())
1309 logger.error(self.next_message())
1310 logger = logging.getLogger("compiler.lexer")
1311 # Both will output a message
1312 logger.info(self.next_message())
1313 logger.error(self.next_message())
1314 # Will not appear
1315 hyphenated.critical(self.next_message())
1316 self.assert_log_lines([
1317 ('INFO', '4'),
1318 ('ERROR', '5'),
1319 ('INFO', '6'),
1320 ('ERROR', '7'),
1321 ], stream=output)
1322 # Original logger output is empty.
1323 self.assert_log_lines([])
1324
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001325
Victor Stinner45df8202010-04-28 22:31:17 +00001326@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001327class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001328
Christian Heimes180510d2008-03-03 19:15:45 +00001329 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001330
Christian Heimes180510d2008-03-03 19:15:45 +00001331 def setUp(self):
1332 """Set up a TCP server to receive log messages, and a SocketHandler
1333 pointing to that server's address and port."""
1334 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001335 addr = ('localhost', 0)
1336 self.server = server = TestTCPServer(addr, self.handle_socket,
1337 0.01)
1338 server.start()
1339 server.ready.wait()
1340 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1341 server.port)
1342 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001343 self.root_logger.removeHandler(self.root_logger.handlers[0])
1344 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001345 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001346
Christian Heimes180510d2008-03-03 19:15:45 +00001347 def tearDown(self):
1348 """Shutdown the TCP server."""
1349 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001350 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001351 self.root_logger.removeHandler(self.sock_hdlr)
1352 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001353 finally:
1354 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001355
Vinay Sajip7367d082011-05-02 13:17:27 +01001356 def handle_socket(self, request):
1357 conn = request.connection
1358 while True:
1359 chunk = conn.recv(4)
1360 if len(chunk) < 4:
1361 break
1362 slen = struct.unpack(">L", chunk)[0]
1363 chunk = conn.recv(slen)
1364 while len(chunk) < slen:
1365 chunk = chunk + conn.recv(slen - len(chunk))
1366 obj = pickle.loads(chunk)
1367 record = logging.makeLogRecord(obj)
1368 self.log_output += record.msg + '\n'
1369 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001370
Christian Heimes180510d2008-03-03 19:15:45 +00001371 def test_output(self):
1372 # The log message sent to the SocketHandler is properly received.
1373 logger = logging.getLogger("tcp")
1374 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001375 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001376 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001377 self.handled.acquire()
1378 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001379
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001380 def test_noserver(self):
1381 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001382 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001383 #The logging call should try to connect, which should fail
1384 try:
1385 raise RuntimeError('Deliberate mistake')
1386 except RuntimeError:
1387 self.root_logger.exception('Never sent')
1388 self.root_logger.error('Never sent, either')
1389 now = time.time()
1390 self.assertTrue(self.sock_hdlr.retryTime > now)
1391 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1392 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001393
Vinay Sajip7367d082011-05-02 13:17:27 +01001394
1395@unittest.skipUnless(threading, 'Threading required for this test.')
1396class DatagramHandlerTest(BaseTest):
1397
1398 """Test for DatagramHandler."""
1399
1400 def setUp(self):
1401 """Set up a UDP server to receive log messages, and a DatagramHandler
1402 pointing to that server's address and port."""
1403 BaseTest.setUp(self)
1404 addr = ('localhost', 0)
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001405 self.server = server = TestUDPServer(addr, self.handle_datagram, 0.01)
Vinay Sajip7367d082011-05-02 13:17:27 +01001406 server.start()
1407 server.ready.wait()
1408 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1409 server.port)
1410 self.log_output = ''
1411 self.root_logger.removeHandler(self.root_logger.handlers[0])
1412 self.root_logger.addHandler(self.sock_hdlr)
1413 self.handled = threading.Event()
1414
1415 def tearDown(self):
1416 """Shutdown the UDP server."""
1417 try:
1418 self.server.stop(2.0)
1419 self.root_logger.removeHandler(self.sock_hdlr)
1420 self.sock_hdlr.close()
1421 finally:
1422 BaseTest.tearDown(self)
1423
1424 def handle_datagram(self, request):
1425 slen = struct.pack('>L', 0) # length of prefix
1426 packet = request.packet[len(slen):]
1427 obj = pickle.loads(packet)
1428 record = logging.makeLogRecord(obj)
1429 self.log_output += record.msg + '\n'
1430 self.handled.set()
1431
1432 def test_output(self):
1433 # The log message sent to the DatagramHandler is properly received.
1434 logger = logging.getLogger("udp")
1435 logger.error("spam")
1436 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001437 self.handled.clear()
1438 logger.error("eggs")
1439 self.handled.wait()
1440 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001441
1442
1443@unittest.skipUnless(threading, 'Threading required for this test.')
1444class SysLogHandlerTest(BaseTest):
1445
1446 """Test for SysLogHandler using UDP."""
1447
1448 def setUp(self):
1449 """Set up a UDP server to receive log messages, and a SysLogHandler
1450 pointing to that server's address and port."""
1451 BaseTest.setUp(self)
1452 addr = ('localhost', 0)
1453 self.server = server = TestUDPServer(addr, self.handle_datagram,
1454 0.01)
1455 server.start()
1456 server.ready.wait()
1457 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1458 server.port))
1459 self.log_output = ''
1460 self.root_logger.removeHandler(self.root_logger.handlers[0])
1461 self.root_logger.addHandler(self.sl_hdlr)
1462 self.handled = threading.Event()
1463
1464 def tearDown(self):
1465 """Shutdown the UDP server."""
1466 try:
1467 self.server.stop(2.0)
1468 self.root_logger.removeHandler(self.sl_hdlr)
1469 self.sl_hdlr.close()
1470 finally:
1471 BaseTest.tearDown(self)
1472
1473 def handle_datagram(self, request):
1474 self.log_output = request.packet
1475 self.handled.set()
1476
1477 def test_output(self):
1478 # The log message sent to the SysLogHandler is properly received.
1479 logger = logging.getLogger("slh")
1480 logger.error("sp\xe4m")
1481 self.handled.wait()
1482 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001483 self.handled.clear()
1484 self.sl_hdlr.append_nul = False
1485 logger.error("sp\xe4m")
1486 self.handled.wait()
1487 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001488 self.handled.clear()
1489 self.sl_hdlr.ident = "h\xe4m-"
1490 logger.error("sp\xe4m")
1491 self.handled.wait()
1492 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfh\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001493
1494
1495@unittest.skipUnless(threading, 'Threading required for this test.')
1496class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001497 """Test for HTTPHandler."""
1498
Vinay Sajip9ba87612011-05-21 11:32:15 +01001499 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1500MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1501zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1502aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1503AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
15040tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
15055LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1506piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
15072z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1508i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1509PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1510E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1511rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1512/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1513-----END RSA PRIVATE KEY-----
1514-----BEGIN CERTIFICATE-----
1515MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1516BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1517EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1518xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1519l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1520vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1521BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1522S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1523BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15244f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15251c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1526IRbTpvlHWPjsSvHz0ZOH
1527-----END CERTIFICATE-----"""
1528
Vinay Sajip7367d082011-05-02 13:17:27 +01001529 def setUp(self):
1530 """Set up an HTTP server to receive log messages, and a HTTPHandler
1531 pointing to that server's address and port."""
1532 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001533 self.handled = threading.Event()
1534
Vinay Sajip7367d082011-05-02 13:17:27 +01001535 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001536 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001537 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001538 if self.command == 'POST':
1539 try:
1540 rlen = int(request.headers['Content-Length'])
1541 self.post_data = request.rfile.read(rlen)
1542 except:
1543 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001544 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001545 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001546 self.handled.set()
1547
1548 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001549 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001550 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001551 root_logger = self.root_logger
1552 root_logger.removeHandler(self.root_logger.handlers[0])
1553 for secure in (False, True):
1554 addr = ('localhost', 0)
1555 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001556 try:
1557 import ssl
1558 fd, fn = tempfile.mkstemp()
1559 os.close(fd)
1560 with open(fn, 'w') as f:
1561 f.write(self.PEMFILE)
1562 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1563 sslctx.load_cert_chain(fn)
1564 os.unlink(fn)
1565 except ImportError:
1566 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001567 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001568 sslctx = None
1569 self.server = server = TestHTTPServer(addr, self.handle_request,
1570 0.01, sslctx=sslctx)
1571 server.start()
1572 server.ready.wait()
1573 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001574 secure_client = secure and sslctx
1575 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1576 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001577 self.log_data = None
1578 root_logger.addHandler(self.h_hdlr)
1579
1580 for method in ('GET', 'POST'):
1581 self.h_hdlr.method = method
1582 self.handled.clear()
1583 msg = "sp\xe4m"
1584 logger.error(msg)
1585 self.handled.wait()
1586 self.assertEqual(self.log_data.path, '/frob')
1587 self.assertEqual(self.command, method)
1588 if method == 'GET':
1589 d = parse_qs(self.log_data.query)
1590 else:
1591 d = parse_qs(self.post_data.decode('utf-8'))
1592 self.assertEqual(d['name'], ['http'])
1593 self.assertEqual(d['funcName'], ['test_output'])
1594 self.assertEqual(d['msg'], [msg])
1595
1596 self.server.stop(2.0)
1597 self.root_logger.removeHandler(self.h_hdlr)
1598 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001599
Christian Heimes180510d2008-03-03 19:15:45 +00001600class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001601
Christian Heimes180510d2008-03-03 19:15:45 +00001602 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001603
Christian Heimes180510d2008-03-03 19:15:45 +00001604 def setUp(self):
1605 """Create a dict to remember potentially destroyed objects."""
1606 BaseTest.setUp(self)
1607 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001608
Christian Heimes180510d2008-03-03 19:15:45 +00001609 def _watch_for_survival(self, *args):
1610 """Watch the given objects for survival, by creating weakrefs to
1611 them."""
1612 for obj in args:
1613 key = id(obj), repr(obj)
1614 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001615
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001616 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001617 """Assert that all objects watched for survival have survived."""
1618 # Trigger cycle breaking.
1619 gc.collect()
1620 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001621 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001622 if ref() is None:
1623 dead.append(repr_)
1624 if dead:
1625 self.fail("%d objects should have survived "
1626 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001627
Christian Heimes180510d2008-03-03 19:15:45 +00001628 def test_persistent_loggers(self):
1629 # Logger objects are persistent and retain their configuration, even
1630 # if visible references are destroyed.
1631 self.root_logger.setLevel(logging.INFO)
1632 foo = logging.getLogger("foo")
1633 self._watch_for_survival(foo)
1634 foo.setLevel(logging.DEBUG)
1635 self.root_logger.debug(self.next_message())
1636 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001637 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001638 ('foo', 'DEBUG', '2'),
1639 ])
1640 del foo
1641 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001642 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001643 # foo has retained its settings.
1644 bar = logging.getLogger("foo")
1645 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001646 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001647 ('foo', 'DEBUG', '2'),
1648 ('foo', 'DEBUG', '3'),
1649 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001650
Benjamin Petersonf91df042009-02-13 02:50:59 +00001651
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001652class EncodingTest(BaseTest):
1653 def test_encoding_plain_file(self):
1654 # In Python 2.x, a plain file object is treated as having no encoding.
1655 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001656 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1657 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001658 # the non-ascii data we write to the log.
1659 data = "foo\x80"
1660 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001661 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001662 log.addHandler(handler)
1663 try:
1664 # write non-ascii data to the log.
1665 log.warning(data)
1666 finally:
1667 log.removeHandler(handler)
1668 handler.close()
1669 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001670 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001671 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001672 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001673 finally:
1674 f.close()
1675 finally:
1676 if os.path.isfile(fn):
1677 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001678
Benjamin Petersonf91df042009-02-13 02:50:59 +00001679 def test_encoding_cyrillic_unicode(self):
1680 log = logging.getLogger("test")
1681 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1682 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1683 #Ensure it's written in a Cyrillic encoding
1684 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001685 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001686 stream = io.BytesIO()
1687 writer = writer_class(stream, 'strict')
1688 handler = logging.StreamHandler(writer)
1689 log.addHandler(handler)
1690 try:
1691 log.warning(message)
1692 finally:
1693 log.removeHandler(handler)
1694 handler.close()
1695 # check we wrote exactly those bytes, ignoring trailing \n etc
1696 s = stream.getvalue()
1697 #Compare against what the data should be when encoded in CP-1251
1698 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1699
1700
Georg Brandlf9734072008-12-07 15:30:06 +00001701class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001702
Georg Brandlf9734072008-12-07 15:30:06 +00001703 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001704 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001705 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001706 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001707 warnings.filterwarnings("always", category=UserWarning)
1708 stream = io.StringIO()
1709 h = logging.StreamHandler(stream)
1710 logger = logging.getLogger("py.warnings")
1711 logger.addHandler(h)
1712 warnings.warn("I'm warning you...")
1713 logger.removeHandler(h)
1714 s = stream.getvalue()
1715 h.close()
1716 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001717
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001718 #See if an explicit file uses the original implementation
1719 a_file = io.StringIO()
1720 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1721 a_file, "Dummy line")
1722 s = a_file.getvalue()
1723 a_file.close()
1724 self.assertEqual(s,
1725 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1726
1727 def test_warnings_no_handlers(self):
1728 with warnings.catch_warnings():
1729 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001730 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001731
1732 # confirm our assumption: no loggers are set
1733 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001734 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001735
1736 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001737 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001738 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001739
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001740
1741def formatFunc(format, datefmt=None):
1742 return logging.Formatter(format, datefmt)
1743
1744def handlerFunc():
1745 return logging.StreamHandler()
1746
1747class CustomHandler(logging.StreamHandler):
1748 pass
1749
1750class ConfigDictTest(BaseTest):
1751
1752 """Reading logging config from a dictionary."""
1753
1754 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1755
1756 # config0 is a standard configuration.
1757 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001758 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001759 'formatters': {
1760 'form1' : {
1761 'format' : '%(levelname)s ++ %(message)s',
1762 },
1763 },
1764 'handlers' : {
1765 'hand1' : {
1766 'class' : 'logging.StreamHandler',
1767 'formatter' : 'form1',
1768 'level' : 'NOTSET',
1769 'stream' : 'ext://sys.stdout',
1770 },
1771 },
1772 'root' : {
1773 'level' : 'WARNING',
1774 'handlers' : ['hand1'],
1775 },
1776 }
1777
1778 # config1 adds a little to the standard configuration.
1779 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001780 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001781 'formatters': {
1782 'form1' : {
1783 'format' : '%(levelname)s ++ %(message)s',
1784 },
1785 },
1786 'handlers' : {
1787 'hand1' : {
1788 'class' : 'logging.StreamHandler',
1789 'formatter' : 'form1',
1790 'level' : 'NOTSET',
1791 'stream' : 'ext://sys.stdout',
1792 },
1793 },
1794 'loggers' : {
1795 'compiler.parser' : {
1796 'level' : 'DEBUG',
1797 'handlers' : ['hand1'],
1798 },
1799 },
1800 'root' : {
1801 'level' : 'WARNING',
1802 },
1803 }
1804
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001805 # config1a moves the handler to the root. Used with config8a
1806 config1a = {
1807 'version': 1,
1808 'formatters': {
1809 'form1' : {
1810 'format' : '%(levelname)s ++ %(message)s',
1811 },
1812 },
1813 'handlers' : {
1814 'hand1' : {
1815 'class' : 'logging.StreamHandler',
1816 'formatter' : 'form1',
1817 'level' : 'NOTSET',
1818 'stream' : 'ext://sys.stdout',
1819 },
1820 },
1821 'loggers' : {
1822 'compiler.parser' : {
1823 'level' : 'DEBUG',
1824 },
1825 },
1826 'root' : {
1827 'level' : 'WARNING',
1828 'handlers' : ['hand1'],
1829 },
1830 }
1831
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001832 # config2 has a subtle configuration error that should be reported
1833 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001834 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001835 'formatters': {
1836 'form1' : {
1837 'format' : '%(levelname)s ++ %(message)s',
1838 },
1839 },
1840 'handlers' : {
1841 'hand1' : {
1842 'class' : 'logging.StreamHandler',
1843 'formatter' : 'form1',
1844 'level' : 'NOTSET',
1845 'stream' : 'ext://sys.stdbout',
1846 },
1847 },
1848 'loggers' : {
1849 'compiler.parser' : {
1850 'level' : 'DEBUG',
1851 'handlers' : ['hand1'],
1852 },
1853 },
1854 'root' : {
1855 'level' : 'WARNING',
1856 },
1857 }
1858
1859 #As config1 but with a misspelt level on a handler
1860 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001861 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001862 'formatters': {
1863 'form1' : {
1864 'format' : '%(levelname)s ++ %(message)s',
1865 },
1866 },
1867 'handlers' : {
1868 'hand1' : {
1869 'class' : 'logging.StreamHandler',
1870 'formatter' : 'form1',
1871 'level' : 'NTOSET',
1872 'stream' : 'ext://sys.stdout',
1873 },
1874 },
1875 'loggers' : {
1876 'compiler.parser' : {
1877 'level' : 'DEBUG',
1878 'handlers' : ['hand1'],
1879 },
1880 },
1881 'root' : {
1882 'level' : 'WARNING',
1883 },
1884 }
1885
1886
1887 #As config1 but with a misspelt level on a logger
1888 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001889 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001890 'formatters': {
1891 'form1' : {
1892 'format' : '%(levelname)s ++ %(message)s',
1893 },
1894 },
1895 'handlers' : {
1896 'hand1' : {
1897 'class' : 'logging.StreamHandler',
1898 'formatter' : 'form1',
1899 'level' : 'NOTSET',
1900 'stream' : 'ext://sys.stdout',
1901 },
1902 },
1903 'loggers' : {
1904 'compiler.parser' : {
1905 'level' : 'DEBUG',
1906 'handlers' : ['hand1'],
1907 },
1908 },
1909 'root' : {
1910 'level' : 'WRANING',
1911 },
1912 }
1913
1914 # config3 has a less subtle configuration error
1915 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001916 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001917 'formatters': {
1918 'form1' : {
1919 'format' : '%(levelname)s ++ %(message)s',
1920 },
1921 },
1922 'handlers' : {
1923 'hand1' : {
1924 'class' : 'logging.StreamHandler',
1925 'formatter' : 'misspelled_name',
1926 'level' : 'NOTSET',
1927 'stream' : 'ext://sys.stdout',
1928 },
1929 },
1930 'loggers' : {
1931 'compiler.parser' : {
1932 'level' : 'DEBUG',
1933 'handlers' : ['hand1'],
1934 },
1935 },
1936 'root' : {
1937 'level' : 'WARNING',
1938 },
1939 }
1940
1941 # config4 specifies a custom formatter class to be loaded
1942 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001943 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001944 'formatters': {
1945 'form1' : {
1946 '()' : __name__ + '.ExceptionFormatter',
1947 'format' : '%(levelname)s:%(name)s:%(message)s',
1948 },
1949 },
1950 'handlers' : {
1951 'hand1' : {
1952 'class' : 'logging.StreamHandler',
1953 'formatter' : 'form1',
1954 'level' : 'NOTSET',
1955 'stream' : 'ext://sys.stdout',
1956 },
1957 },
1958 'root' : {
1959 'level' : 'NOTSET',
1960 'handlers' : ['hand1'],
1961 },
1962 }
1963
1964 # As config4 but using an actual callable rather than a string
1965 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001966 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001967 'formatters': {
1968 'form1' : {
1969 '()' : ExceptionFormatter,
1970 'format' : '%(levelname)s:%(name)s:%(message)s',
1971 },
1972 'form2' : {
1973 '()' : __name__ + '.formatFunc',
1974 'format' : '%(levelname)s:%(name)s:%(message)s',
1975 },
1976 'form3' : {
1977 '()' : formatFunc,
1978 'format' : '%(levelname)s:%(name)s:%(message)s',
1979 },
1980 },
1981 'handlers' : {
1982 'hand1' : {
1983 'class' : 'logging.StreamHandler',
1984 'formatter' : 'form1',
1985 'level' : 'NOTSET',
1986 'stream' : 'ext://sys.stdout',
1987 },
1988 'hand2' : {
1989 '()' : handlerFunc,
1990 },
1991 },
1992 'root' : {
1993 'level' : 'NOTSET',
1994 'handlers' : ['hand1'],
1995 },
1996 }
1997
1998 # config5 specifies a custom handler class to be loaded
1999 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002000 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002001 'formatters': {
2002 'form1' : {
2003 'format' : '%(levelname)s ++ %(message)s',
2004 },
2005 },
2006 'handlers' : {
2007 'hand1' : {
2008 'class' : __name__ + '.CustomHandler',
2009 'formatter' : 'form1',
2010 'level' : 'NOTSET',
2011 'stream' : 'ext://sys.stdout',
2012 },
2013 },
2014 'loggers' : {
2015 'compiler.parser' : {
2016 'level' : 'DEBUG',
2017 'handlers' : ['hand1'],
2018 },
2019 },
2020 'root' : {
2021 'level' : 'WARNING',
2022 },
2023 }
2024
2025 # config6 specifies a custom handler class to be loaded
2026 # but has bad arguments
2027 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002028 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002029 'formatters': {
2030 'form1' : {
2031 'format' : '%(levelname)s ++ %(message)s',
2032 },
2033 },
2034 'handlers' : {
2035 'hand1' : {
2036 'class' : __name__ + '.CustomHandler',
2037 'formatter' : 'form1',
2038 'level' : 'NOTSET',
2039 'stream' : 'ext://sys.stdout',
2040 '9' : 'invalid parameter name',
2041 },
2042 },
2043 'loggers' : {
2044 'compiler.parser' : {
2045 'level' : 'DEBUG',
2046 'handlers' : ['hand1'],
2047 },
2048 },
2049 'root' : {
2050 'level' : 'WARNING',
2051 },
2052 }
2053
2054 #config 7 does not define compiler.parser but defines compiler.lexer
2055 #so compiler.parser should be disabled after applying it
2056 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002057 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002058 'formatters': {
2059 'form1' : {
2060 'format' : '%(levelname)s ++ %(message)s',
2061 },
2062 },
2063 'handlers' : {
2064 'hand1' : {
2065 'class' : 'logging.StreamHandler',
2066 'formatter' : 'form1',
2067 'level' : 'NOTSET',
2068 'stream' : 'ext://sys.stdout',
2069 },
2070 },
2071 'loggers' : {
2072 'compiler.lexer' : {
2073 'level' : 'DEBUG',
2074 'handlers' : ['hand1'],
2075 },
2076 },
2077 'root' : {
2078 'level' : 'WARNING',
2079 },
2080 }
2081
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002082 # config8 defines both compiler and compiler.lexer
2083 # so compiler.parser should not be disabled (since
2084 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002085 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002086 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002087 'disable_existing_loggers' : False,
2088 'formatters': {
2089 'form1' : {
2090 'format' : '%(levelname)s ++ %(message)s',
2091 },
2092 },
2093 'handlers' : {
2094 'hand1' : {
2095 'class' : 'logging.StreamHandler',
2096 'formatter' : 'form1',
2097 'level' : 'NOTSET',
2098 'stream' : 'ext://sys.stdout',
2099 },
2100 },
2101 'loggers' : {
2102 'compiler' : {
2103 'level' : 'DEBUG',
2104 'handlers' : ['hand1'],
2105 },
2106 'compiler.lexer' : {
2107 },
2108 },
2109 'root' : {
2110 'level' : 'WARNING',
2111 },
2112 }
2113
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002114 # config8a disables existing loggers
2115 config8a = {
2116 'version': 1,
2117 'disable_existing_loggers' : True,
2118 'formatters': {
2119 'form1' : {
2120 'format' : '%(levelname)s ++ %(message)s',
2121 },
2122 },
2123 'handlers' : {
2124 'hand1' : {
2125 'class' : 'logging.StreamHandler',
2126 'formatter' : 'form1',
2127 'level' : 'NOTSET',
2128 'stream' : 'ext://sys.stdout',
2129 },
2130 },
2131 'loggers' : {
2132 'compiler' : {
2133 'level' : 'DEBUG',
2134 'handlers' : ['hand1'],
2135 },
2136 'compiler.lexer' : {
2137 },
2138 },
2139 'root' : {
2140 'level' : 'WARNING',
2141 },
2142 }
2143
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002144 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002145 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002146 'formatters': {
2147 'form1' : {
2148 'format' : '%(levelname)s ++ %(message)s',
2149 },
2150 },
2151 'handlers' : {
2152 'hand1' : {
2153 'class' : 'logging.StreamHandler',
2154 'formatter' : 'form1',
2155 'level' : 'WARNING',
2156 'stream' : 'ext://sys.stdout',
2157 },
2158 },
2159 'loggers' : {
2160 'compiler.parser' : {
2161 'level' : 'WARNING',
2162 'handlers' : ['hand1'],
2163 },
2164 },
2165 'root' : {
2166 'level' : 'NOTSET',
2167 },
2168 }
2169
2170 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002171 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002172 'incremental' : True,
2173 'handlers' : {
2174 'hand1' : {
2175 'level' : 'WARNING',
2176 },
2177 },
2178 'loggers' : {
2179 'compiler.parser' : {
2180 'level' : 'INFO',
2181 },
2182 },
2183 }
2184
2185 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002186 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002187 'incremental' : True,
2188 'handlers' : {
2189 'hand1' : {
2190 'level' : 'INFO',
2191 },
2192 },
2193 'loggers' : {
2194 'compiler.parser' : {
2195 'level' : 'INFO',
2196 },
2197 },
2198 }
2199
2200 #As config1 but with a filter added
2201 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002202 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002203 'formatters': {
2204 'form1' : {
2205 'format' : '%(levelname)s ++ %(message)s',
2206 },
2207 },
2208 'filters' : {
2209 'filt1' : {
2210 'name' : 'compiler.parser',
2211 },
2212 },
2213 'handlers' : {
2214 'hand1' : {
2215 'class' : 'logging.StreamHandler',
2216 'formatter' : 'form1',
2217 'level' : 'NOTSET',
2218 'stream' : 'ext://sys.stdout',
2219 'filters' : ['filt1'],
2220 },
2221 },
2222 'loggers' : {
2223 'compiler.parser' : {
2224 'level' : 'DEBUG',
2225 'filters' : ['filt1'],
2226 },
2227 },
2228 'root' : {
2229 'level' : 'WARNING',
2230 'handlers' : ['hand1'],
2231 },
2232 }
2233
2234 #As config1 but using cfg:// references
2235 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002236 'version': 1,
2237 'true_formatters': {
2238 'form1' : {
2239 'format' : '%(levelname)s ++ %(message)s',
2240 },
2241 },
2242 'handler_configs': {
2243 'hand1' : {
2244 'class' : 'logging.StreamHandler',
2245 'formatter' : 'form1',
2246 'level' : 'NOTSET',
2247 'stream' : 'ext://sys.stdout',
2248 },
2249 },
2250 'formatters' : 'cfg://true_formatters',
2251 'handlers' : {
2252 'hand1' : 'cfg://handler_configs[hand1]',
2253 },
2254 'loggers' : {
2255 'compiler.parser' : {
2256 'level' : 'DEBUG',
2257 'handlers' : ['hand1'],
2258 },
2259 },
2260 'root' : {
2261 'level' : 'WARNING',
2262 },
2263 }
2264
2265 #As config11 but missing the version key
2266 config12 = {
2267 'true_formatters': {
2268 'form1' : {
2269 'format' : '%(levelname)s ++ %(message)s',
2270 },
2271 },
2272 'handler_configs': {
2273 'hand1' : {
2274 'class' : 'logging.StreamHandler',
2275 'formatter' : 'form1',
2276 'level' : 'NOTSET',
2277 'stream' : 'ext://sys.stdout',
2278 },
2279 },
2280 'formatters' : 'cfg://true_formatters',
2281 'handlers' : {
2282 'hand1' : 'cfg://handler_configs[hand1]',
2283 },
2284 'loggers' : {
2285 'compiler.parser' : {
2286 'level' : 'DEBUG',
2287 'handlers' : ['hand1'],
2288 },
2289 },
2290 'root' : {
2291 'level' : 'WARNING',
2292 },
2293 }
2294
2295 #As config11 but using an unsupported version
2296 config13 = {
2297 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002298 'true_formatters': {
2299 'form1' : {
2300 'format' : '%(levelname)s ++ %(message)s',
2301 },
2302 },
2303 'handler_configs': {
2304 'hand1' : {
2305 'class' : 'logging.StreamHandler',
2306 'formatter' : 'form1',
2307 'level' : 'NOTSET',
2308 'stream' : 'ext://sys.stdout',
2309 },
2310 },
2311 'formatters' : 'cfg://true_formatters',
2312 'handlers' : {
2313 'hand1' : 'cfg://handler_configs[hand1]',
2314 },
2315 'loggers' : {
2316 'compiler.parser' : {
2317 'level' : 'DEBUG',
2318 'handlers' : ['hand1'],
2319 },
2320 },
2321 'root' : {
2322 'level' : 'WARNING',
2323 },
2324 }
2325
2326 def apply_config(self, conf):
2327 logging.config.dictConfig(conf)
2328
2329 def test_config0_ok(self):
2330 # A simple config which overrides the default settings.
2331 with captured_stdout() as output:
2332 self.apply_config(self.config0)
2333 logger = logging.getLogger()
2334 # Won't output anything
2335 logger.info(self.next_message())
2336 # Outputs a message
2337 logger.error(self.next_message())
2338 self.assert_log_lines([
2339 ('ERROR', '2'),
2340 ], stream=output)
2341 # Original logger output is empty.
2342 self.assert_log_lines([])
2343
2344 def test_config1_ok(self, config=config1):
2345 # A config defining a sub-parser as well.
2346 with captured_stdout() as output:
2347 self.apply_config(config)
2348 logger = logging.getLogger("compiler.parser")
2349 # Both will output a message
2350 logger.info(self.next_message())
2351 logger.error(self.next_message())
2352 self.assert_log_lines([
2353 ('INFO', '1'),
2354 ('ERROR', '2'),
2355 ], stream=output)
2356 # Original logger output is empty.
2357 self.assert_log_lines([])
2358
2359 def test_config2_failure(self):
2360 # A simple config which overrides the default settings.
2361 self.assertRaises(Exception, self.apply_config, self.config2)
2362
2363 def test_config2a_failure(self):
2364 # A simple config which overrides the default settings.
2365 self.assertRaises(Exception, self.apply_config, self.config2a)
2366
2367 def test_config2b_failure(self):
2368 # A simple config which overrides the default settings.
2369 self.assertRaises(Exception, self.apply_config, self.config2b)
2370
2371 def test_config3_failure(self):
2372 # A simple config which overrides the default settings.
2373 self.assertRaises(Exception, self.apply_config, self.config3)
2374
2375 def test_config4_ok(self):
2376 # A config specifying a custom formatter class.
2377 with captured_stdout() as output:
2378 self.apply_config(self.config4)
2379 #logger = logging.getLogger()
2380 try:
2381 raise RuntimeError()
2382 except RuntimeError:
2383 logging.exception("just testing")
2384 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002385 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002386 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2387 # Original logger output is empty
2388 self.assert_log_lines([])
2389
2390 def test_config4a_ok(self):
2391 # A config specifying a custom formatter class.
2392 with captured_stdout() as output:
2393 self.apply_config(self.config4a)
2394 #logger = logging.getLogger()
2395 try:
2396 raise RuntimeError()
2397 except RuntimeError:
2398 logging.exception("just testing")
2399 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002400 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002401 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2402 # Original logger output is empty
2403 self.assert_log_lines([])
2404
2405 def test_config5_ok(self):
2406 self.test_config1_ok(config=self.config5)
2407
2408 def test_config6_failure(self):
2409 self.assertRaises(Exception, self.apply_config, self.config6)
2410
2411 def test_config7_ok(self):
2412 with captured_stdout() as output:
2413 self.apply_config(self.config1)
2414 logger = logging.getLogger("compiler.parser")
2415 # Both will output a message
2416 logger.info(self.next_message())
2417 logger.error(self.next_message())
2418 self.assert_log_lines([
2419 ('INFO', '1'),
2420 ('ERROR', '2'),
2421 ], stream=output)
2422 # Original logger output is empty.
2423 self.assert_log_lines([])
2424 with captured_stdout() as output:
2425 self.apply_config(self.config7)
2426 logger = logging.getLogger("compiler.parser")
2427 self.assertTrue(logger.disabled)
2428 logger = logging.getLogger("compiler.lexer")
2429 # Both will output a message
2430 logger.info(self.next_message())
2431 logger.error(self.next_message())
2432 self.assert_log_lines([
2433 ('INFO', '3'),
2434 ('ERROR', '4'),
2435 ], stream=output)
2436 # Original logger output is empty.
2437 self.assert_log_lines([])
2438
2439 #Same as test_config_7_ok but don't disable old loggers.
2440 def test_config_8_ok(self):
2441 with captured_stdout() as output:
2442 self.apply_config(self.config1)
2443 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002444 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002445 logger.info(self.next_message())
2446 logger.error(self.next_message())
2447 self.assert_log_lines([
2448 ('INFO', '1'),
2449 ('ERROR', '2'),
2450 ], stream=output)
2451 # Original logger output is empty.
2452 self.assert_log_lines([])
2453 with captured_stdout() as output:
2454 self.apply_config(self.config8)
2455 logger = logging.getLogger("compiler.parser")
2456 self.assertFalse(logger.disabled)
2457 # Both will output a message
2458 logger.info(self.next_message())
2459 logger.error(self.next_message())
2460 logger = logging.getLogger("compiler.lexer")
2461 # Both will output a message
2462 logger.info(self.next_message())
2463 logger.error(self.next_message())
2464 self.assert_log_lines([
2465 ('INFO', '3'),
2466 ('ERROR', '4'),
2467 ('INFO', '5'),
2468 ('ERROR', '6'),
2469 ], stream=output)
2470 # Original logger output is empty.
2471 self.assert_log_lines([])
2472
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002473 def test_config_8a_ok(self):
2474 with captured_stdout() as output:
2475 self.apply_config(self.config1a)
2476 logger = logging.getLogger("compiler.parser")
2477 # See issue #11424. compiler-hyphenated sorts
2478 # between compiler and compiler.xyz and this
2479 # was preventing compiler.xyz from being included
2480 # in the child loggers of compiler because of an
2481 # overzealous loop termination condition.
2482 hyphenated = logging.getLogger('compiler-hyphenated')
2483 # All will output a message
2484 logger.info(self.next_message())
2485 logger.error(self.next_message())
2486 hyphenated.critical(self.next_message())
2487 self.assert_log_lines([
2488 ('INFO', '1'),
2489 ('ERROR', '2'),
2490 ('CRITICAL', '3'),
2491 ], stream=output)
2492 # Original logger output is empty.
2493 self.assert_log_lines([])
2494 with captured_stdout() as output:
2495 self.apply_config(self.config8a)
2496 logger = logging.getLogger("compiler.parser")
2497 self.assertFalse(logger.disabled)
2498 # Both will output a message
2499 logger.info(self.next_message())
2500 logger.error(self.next_message())
2501 logger = logging.getLogger("compiler.lexer")
2502 # Both will output a message
2503 logger.info(self.next_message())
2504 logger.error(self.next_message())
2505 # Will not appear
2506 hyphenated.critical(self.next_message())
2507 self.assert_log_lines([
2508 ('INFO', '4'),
2509 ('ERROR', '5'),
2510 ('INFO', '6'),
2511 ('ERROR', '7'),
2512 ], stream=output)
2513 # Original logger output is empty.
2514 self.assert_log_lines([])
2515
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002516 def test_config_9_ok(self):
2517 with captured_stdout() as output:
2518 self.apply_config(self.config9)
2519 logger = logging.getLogger("compiler.parser")
2520 #Nothing will be output since both handler and logger are set to WARNING
2521 logger.info(self.next_message())
2522 self.assert_log_lines([], stream=output)
2523 self.apply_config(self.config9a)
2524 #Nothing will be output since both handler is still set to WARNING
2525 logger.info(self.next_message())
2526 self.assert_log_lines([], stream=output)
2527 self.apply_config(self.config9b)
2528 #Message should now be output
2529 logger.info(self.next_message())
2530 self.assert_log_lines([
2531 ('INFO', '3'),
2532 ], stream=output)
2533
2534 def test_config_10_ok(self):
2535 with captured_stdout() as output:
2536 self.apply_config(self.config10)
2537 logger = logging.getLogger("compiler.parser")
2538 logger.warning(self.next_message())
2539 logger = logging.getLogger('compiler')
2540 #Not output, because filtered
2541 logger.warning(self.next_message())
2542 logger = logging.getLogger('compiler.lexer')
2543 #Not output, because filtered
2544 logger.warning(self.next_message())
2545 logger = logging.getLogger("compiler.parser.codegen")
2546 #Output, as not filtered
2547 logger.error(self.next_message())
2548 self.assert_log_lines([
2549 ('WARNING', '1'),
2550 ('ERROR', '4'),
2551 ], stream=output)
2552
2553 def test_config11_ok(self):
2554 self.test_config1_ok(self.config11)
2555
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002556 def test_config12_failure(self):
2557 self.assertRaises(Exception, self.apply_config, self.config12)
2558
2559 def test_config13_failure(self):
2560 self.assertRaises(Exception, self.apply_config, self.config13)
2561
Victor Stinner45df8202010-04-28 22:31:17 +00002562 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002563 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002564 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002565 # Ask for a randomly assigned port (by using port 0)
2566 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002567 t.start()
2568 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002569 # Now get the port allocated
2570 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002571 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002572 try:
2573 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2574 sock.settimeout(2.0)
2575 sock.connect(('localhost', port))
2576
2577 slen = struct.pack('>L', len(text))
2578 s = slen + text
2579 sentsofar = 0
2580 left = len(s)
2581 while left > 0:
2582 sent = sock.send(s[sentsofar:])
2583 sentsofar += sent
2584 left -= sent
2585 sock.close()
2586 finally:
2587 t.ready.wait(2.0)
2588 logging.config.stopListening()
2589 t.join(2.0)
2590
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002591 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002592 def test_listen_config_10_ok(self):
2593 with captured_stdout() as output:
2594 self.setup_via_listener(json.dumps(self.config10))
2595 logger = logging.getLogger("compiler.parser")
2596 logger.warning(self.next_message())
2597 logger = logging.getLogger('compiler')
2598 #Not output, because filtered
2599 logger.warning(self.next_message())
2600 logger = logging.getLogger('compiler.lexer')
2601 #Not output, because filtered
2602 logger.warning(self.next_message())
2603 logger = logging.getLogger("compiler.parser.codegen")
2604 #Output, as not filtered
2605 logger.error(self.next_message())
2606 self.assert_log_lines([
2607 ('WARNING', '1'),
2608 ('ERROR', '4'),
2609 ], stream=output)
2610
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002611 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002612 def test_listen_config_1_ok(self):
2613 with captured_stdout() as output:
2614 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2615 logger = logging.getLogger("compiler.parser")
2616 # Both will output a message
2617 logger.info(self.next_message())
2618 logger.error(self.next_message())
2619 self.assert_log_lines([
2620 ('INFO', '1'),
2621 ('ERROR', '2'),
2622 ], stream=output)
2623 # Original logger output is empty.
2624 self.assert_log_lines([])
2625
Vinay Sajip373baef2011-04-26 20:05:24 +01002626 def test_baseconfig(self):
2627 d = {
2628 'atuple': (1, 2, 3),
2629 'alist': ['a', 'b', 'c'],
2630 'adict': {'d': 'e', 'f': 3 },
2631 'nest1': ('g', ('h', 'i'), 'j'),
2632 'nest2': ['k', ['l', 'm'], 'n'],
2633 'nest3': ['o', 'cfg://alist', 'p'],
2634 }
2635 bc = logging.config.BaseConfigurator(d)
2636 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2637 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2638 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2639 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2640 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2641 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2642 v = bc.convert('cfg://nest3')
2643 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2644 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2645 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2646 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002647
2648class ManagerTest(BaseTest):
2649 def test_manager_loggerclass(self):
2650 logged = []
2651
2652 class MyLogger(logging.Logger):
2653 def _log(self, level, msg, args, exc_info=None, extra=None):
2654 logged.append(msg)
2655
2656 man = logging.Manager(None)
2657 self.assertRaises(TypeError, man.setLoggerClass, int)
2658 man.setLoggerClass(MyLogger)
2659 logger = man.getLogger('test')
2660 logger.warning('should appear in logged')
2661 logging.warning('should not appear in logged')
2662
2663 self.assertEqual(logged, ['should appear in logged'])
2664
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002665 def test_set_log_record_factory(self):
2666 man = logging.Manager(None)
2667 expected = object()
2668 man.setLogRecordFactory(expected)
2669 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002670
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002671class ChildLoggerTest(BaseTest):
2672 def test_child_loggers(self):
2673 r = logging.getLogger()
2674 l1 = logging.getLogger('abc')
2675 l2 = logging.getLogger('def.ghi')
2676 c1 = r.getChild('xyz')
2677 c2 = r.getChild('uvw.xyz')
2678 self.assertTrue(c1 is logging.getLogger('xyz'))
2679 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2680 c1 = l1.getChild('def')
2681 c2 = c1.getChild('ghi')
2682 c3 = l1.getChild('def.ghi')
2683 self.assertTrue(c1 is logging.getLogger('abc.def'))
2684 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2685 self.assertTrue(c2 is c3)
2686
2687
Vinay Sajip6fac8172010-10-19 20:44:14 +00002688class DerivedLogRecord(logging.LogRecord):
2689 pass
2690
Vinay Sajip61561522010-12-03 11:50:38 +00002691class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002692
2693 def setUp(self):
2694 class CheckingFilter(logging.Filter):
2695 def __init__(self, cls):
2696 self.cls = cls
2697
2698 def filter(self, record):
2699 t = type(record)
2700 if t is not self.cls:
2701 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2702 self.cls)
2703 raise TypeError(msg)
2704 return True
2705
2706 BaseTest.setUp(self)
2707 self.filter = CheckingFilter(DerivedLogRecord)
2708 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002709 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002710
2711 def tearDown(self):
2712 self.root_logger.removeFilter(self.filter)
2713 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002714 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002715
2716 def test_logrecord_class(self):
2717 self.assertRaises(TypeError, self.root_logger.warning,
2718 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002719 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002720 self.root_logger.error(self.next_message())
2721 self.assert_log_lines([
2722 ('root', 'ERROR', '2'),
2723 ])
2724
2725
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002726class QueueHandlerTest(BaseTest):
2727 # Do not bother with a logger name group.
2728 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2729
2730 def setUp(self):
2731 BaseTest.setUp(self)
2732 self.queue = queue.Queue(-1)
2733 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2734 self.que_logger = logging.getLogger('que')
2735 self.que_logger.propagate = False
2736 self.que_logger.setLevel(logging.WARNING)
2737 self.que_logger.addHandler(self.que_hdlr)
2738
2739 def tearDown(self):
2740 self.que_hdlr.close()
2741 BaseTest.tearDown(self)
2742
2743 def test_queue_handler(self):
2744 self.que_logger.debug(self.next_message())
2745 self.assertRaises(queue.Empty, self.queue.get_nowait)
2746 self.que_logger.info(self.next_message())
2747 self.assertRaises(queue.Empty, self.queue.get_nowait)
2748 msg = self.next_message()
2749 self.que_logger.warning(msg)
2750 data = self.queue.get_nowait()
2751 self.assertTrue(isinstance(data, logging.LogRecord))
2752 self.assertEqual(data.name, self.que_logger.name)
2753 self.assertEqual((data.msg, data.args), (msg, None))
2754
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002755 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2756 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002757 def test_queue_listener(self):
2758 handler = TestHandler(Matcher())
2759 listener = logging.handlers.QueueListener(self.queue, handler)
2760 listener.start()
2761 try:
2762 self.que_logger.warning(self.next_message())
2763 self.que_logger.error(self.next_message())
2764 self.que_logger.critical(self.next_message())
2765 finally:
2766 listener.stop()
2767 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2768 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2769 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2770
Vinay Sajip37eb3382011-04-26 20:26:41 +01002771ZERO = datetime.timedelta(0)
2772
2773class UTC(datetime.tzinfo):
2774 def utcoffset(self, dt):
2775 return ZERO
2776
2777 dst = utcoffset
2778
2779 def tzname(self, dt):
2780 return 'UTC'
2781
2782utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002783
Vinay Sajipa39c5712010-10-25 13:57:39 +00002784class FormatterTest(unittest.TestCase):
2785 def setUp(self):
2786 self.common = {
2787 'name': 'formatter.test',
2788 'level': logging.DEBUG,
2789 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2790 'lineno': 42,
2791 'exc_info': None,
2792 'func': None,
2793 'msg': 'Message with %d %s',
2794 'args': (2, 'placeholders'),
2795 }
2796 self.variants = {
2797 }
2798
2799 def get_record(self, name=None):
2800 result = dict(self.common)
2801 if name is not None:
2802 result.update(self.variants[name])
2803 return logging.makeLogRecord(result)
2804
2805 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002806 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002807 r = self.get_record()
2808 f = logging.Formatter('${%(message)s}')
2809 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2810 f = logging.Formatter('%(random)s')
2811 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002812 self.assertFalse(f.usesTime())
2813 f = logging.Formatter('%(asctime)s')
2814 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002815 f = logging.Formatter('%(asctime)-15s')
2816 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002817 f = logging.Formatter('asctime')
2818 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002819
2820 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002821 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002822 r = self.get_record()
2823 f = logging.Formatter('$%{message}%$', style='{')
2824 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2825 f = logging.Formatter('{random}', style='{')
2826 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002827 self.assertFalse(f.usesTime())
2828 f = logging.Formatter('{asctime}', style='{')
2829 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002830 f = logging.Formatter('{asctime!s:15}', style='{')
2831 self.assertTrue(f.usesTime())
2832 f = logging.Formatter('{asctime:15}', style='{')
2833 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002834 f = logging.Formatter('asctime', style='{')
2835 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002836
2837 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002838 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002839 r = self.get_record()
2840 f = logging.Formatter('$message', style='$')
2841 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2842 f = logging.Formatter('$$%${message}%$$', style='$')
2843 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2844 f = logging.Formatter('${random}', style='$')
2845 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002846 self.assertFalse(f.usesTime())
2847 f = logging.Formatter('${asctime}', style='$')
2848 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002849 f = logging.Formatter('${asctime', style='$')
2850 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002851 f = logging.Formatter('$asctime', style='$')
2852 self.assertTrue(f.usesTime())
2853 f = logging.Formatter('asctime', style='$')
2854 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002855
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002856 def test_invalid_style(self):
2857 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2858
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002859 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002860 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002861 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002862 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002863 r.msecs = 123
2864 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002865 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002866 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2867 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002868 f.format(r)
2869 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2870
2871class TestBufferingFormatter(logging.BufferingFormatter):
2872 def formatHeader(self, records):
2873 return '[(%d)' % len(records)
2874
2875 def formatFooter(self, records):
2876 return '(%d)]' % len(records)
2877
2878class BufferingFormatterTest(unittest.TestCase):
2879 def setUp(self):
2880 self.records = [
2881 logging.makeLogRecord({'msg': 'one'}),
2882 logging.makeLogRecord({'msg': 'two'}),
2883 ]
2884
2885 def test_default(self):
2886 f = logging.BufferingFormatter()
2887 self.assertEqual('', f.format([]))
2888 self.assertEqual('onetwo', f.format(self.records))
2889
2890 def test_custom(self):
2891 f = TestBufferingFormatter()
2892 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2893 lf = logging.Formatter('<%(message)s>')
2894 f = TestBufferingFormatter(lf)
2895 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002896
2897class ExceptionTest(BaseTest):
2898 def test_formatting(self):
2899 r = self.root_logger
2900 h = RecordingHandler()
2901 r.addHandler(h)
2902 try:
2903 raise RuntimeError('deliberate mistake')
2904 except:
2905 logging.exception('failed', stack_info=True)
2906 r.removeHandler(h)
2907 h.close()
2908 r = h.records[0]
2909 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2910 'call last):\n'))
2911 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2912 'deliberate mistake'))
2913 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2914 'call last):\n'))
2915 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2916 'stack_info=True)'))
2917
2918
Vinay Sajip5a27d402010-12-10 11:42:57 +00002919class LastResortTest(BaseTest):
2920 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002921 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002922 root = self.root_logger
2923 root.removeHandler(self.root_hdlr)
2924 old_stderr = sys.stderr
2925 old_lastresort = logging.lastResort
2926 old_raise_exceptions = logging.raiseExceptions
2927 try:
2928 sys.stderr = sio = io.StringIO()
Vinay Sajip8188f582011-07-25 19:58:13 +01002929 root.debug('This should not appear')
2930 self.assertEqual(sio.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00002931 root.warning('This is your final chance!')
2932 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2933 #No handlers and no last resort, so 'No handlers' message
2934 logging.lastResort = None
2935 sys.stderr = sio = io.StringIO()
2936 root.warning('This is your final chance!')
2937 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2938 # 'No handlers' message only printed once
2939 sys.stderr = sio = io.StringIO()
2940 root.warning('This is your final chance!')
2941 self.assertEqual(sio.getvalue(), '')
2942 root.manager.emittedNoHandlerWarning = False
2943 #If raiseExceptions is False, no message is printed
2944 logging.raiseExceptions = False
2945 sys.stderr = sio = io.StringIO()
2946 root.warning('This is your final chance!')
2947 self.assertEqual(sio.getvalue(), '')
2948 finally:
2949 sys.stderr = old_stderr
2950 root.addHandler(self.root_hdlr)
2951 logging.lastResort = old_lastresort
2952 logging.raiseExceptions = old_raise_exceptions
2953
2954
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002955class FakeHandler:
2956
2957 def __init__(self, identifier, called):
2958 for method in ('acquire', 'flush', 'close', 'release'):
2959 setattr(self, method, self.record_call(identifier, method, called))
2960
2961 def record_call(self, identifier, method_name, called):
2962 def inner():
2963 called.append('{} - {}'.format(identifier, method_name))
2964 return inner
2965
2966
2967class RecordingHandler(logging.NullHandler):
2968
2969 def __init__(self, *args, **kwargs):
2970 super(RecordingHandler, self).__init__(*args, **kwargs)
2971 self.records = []
2972
2973 def handle(self, record):
2974 """Keep track of all the emitted records."""
2975 self.records.append(record)
2976
2977
2978class ShutdownTest(BaseTest):
2979
Vinay Sajip5e66b162011-04-20 15:41:14 +01002980 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002981
2982 def setUp(self):
2983 super(ShutdownTest, self).setUp()
2984 self.called = []
2985
2986 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002987 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002988
2989 def raise_error(self, error):
2990 def inner():
2991 raise error()
2992 return inner
2993
2994 def test_no_failure(self):
2995 # create some fake handlers
2996 handler0 = FakeHandler(0, self.called)
2997 handler1 = FakeHandler(1, self.called)
2998 handler2 = FakeHandler(2, self.called)
2999
3000 # create live weakref to those handlers
3001 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
3002
3003 logging.shutdown(handlerList=list(handlers))
3004
3005 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
3006 '1 - acquire', '1 - flush', '1 - close', '1 - release',
3007 '0 - acquire', '0 - flush', '0 - close', '0 - release']
3008 self.assertEqual(expected, self.called)
3009
3010 def _test_with_failure_in_method(self, method, error):
3011 handler = FakeHandler(0, self.called)
3012 setattr(handler, method, self.raise_error(error))
3013 handlers = [logging.weakref.ref(handler)]
3014
3015 logging.shutdown(handlerList=list(handlers))
3016
3017 self.assertEqual('0 - release', self.called[-1])
3018
3019 def test_with_ioerror_in_acquire(self):
3020 self._test_with_failure_in_method('acquire', IOError)
3021
3022 def test_with_ioerror_in_flush(self):
3023 self._test_with_failure_in_method('flush', IOError)
3024
3025 def test_with_ioerror_in_close(self):
3026 self._test_with_failure_in_method('close', IOError)
3027
3028 def test_with_valueerror_in_acquire(self):
3029 self._test_with_failure_in_method('acquire', ValueError)
3030
3031 def test_with_valueerror_in_flush(self):
3032 self._test_with_failure_in_method('flush', ValueError)
3033
3034 def test_with_valueerror_in_close(self):
3035 self._test_with_failure_in_method('close', ValueError)
3036
3037 def test_with_other_error_in_acquire_without_raise(self):
3038 logging.raiseExceptions = False
3039 self._test_with_failure_in_method('acquire', IndexError)
3040
3041 def test_with_other_error_in_flush_without_raise(self):
3042 logging.raiseExceptions = False
3043 self._test_with_failure_in_method('flush', IndexError)
3044
3045 def test_with_other_error_in_close_without_raise(self):
3046 logging.raiseExceptions = False
3047 self._test_with_failure_in_method('close', IndexError)
3048
3049 def test_with_other_error_in_acquire_with_raise(self):
3050 logging.raiseExceptions = True
3051 self.assertRaises(IndexError, self._test_with_failure_in_method,
3052 'acquire', IndexError)
3053
3054 def test_with_other_error_in_flush_with_raise(self):
3055 logging.raiseExceptions = True
3056 self.assertRaises(IndexError, self._test_with_failure_in_method,
3057 'flush', IndexError)
3058
3059 def test_with_other_error_in_close_with_raise(self):
3060 logging.raiseExceptions = True
3061 self.assertRaises(IndexError, self._test_with_failure_in_method,
3062 'close', IndexError)
3063
3064
3065class ModuleLevelMiscTest(BaseTest):
3066
Vinay Sajip5e66b162011-04-20 15:41:14 +01003067 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003068
3069 def test_disable(self):
3070 old_disable = logging.root.manager.disable
3071 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003072 self.assertEqual(old_disable, 0)
3073 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003074
3075 logging.disable(83)
3076 self.assertEqual(logging.root.manager.disable, 83)
3077
3078 def _test_log(self, method, level=None):
3079 called = []
3080 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003081 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003082
3083 recording = RecordingHandler()
3084 logging.root.addHandler(recording)
3085
3086 log_method = getattr(logging, method)
3087 if level is not None:
3088 log_method(level, "test me: %r", recording)
3089 else:
3090 log_method("test me: %r", recording)
3091
3092 self.assertEqual(len(recording.records), 1)
3093 record = recording.records[0]
3094 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3095
3096 expected_level = level if level is not None else getattr(logging, method.upper())
3097 self.assertEqual(record.levelno, expected_level)
3098
3099 # basicConfig was not called!
3100 self.assertEqual(called, [])
3101
3102 def test_log(self):
3103 self._test_log('log', logging.ERROR)
3104
3105 def test_debug(self):
3106 self._test_log('debug')
3107
3108 def test_info(self):
3109 self._test_log('info')
3110
3111 def test_warning(self):
3112 self._test_log('warning')
3113
3114 def test_error(self):
3115 self._test_log('error')
3116
3117 def test_critical(self):
3118 self._test_log('critical')
3119
3120 def test_set_logger_class(self):
3121 self.assertRaises(TypeError, logging.setLoggerClass, object)
3122
3123 class MyLogger(logging.Logger):
3124 pass
3125
3126 logging.setLoggerClass(MyLogger)
3127 self.assertEqual(logging.getLoggerClass(), MyLogger)
3128
3129 logging.setLoggerClass(logging.Logger)
3130 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3131
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003132class LogRecordTest(BaseTest):
3133 def test_str_rep(self):
3134 r = logging.makeLogRecord({})
3135 s = str(r)
3136 self.assertTrue(s.startswith('<LogRecord: '))
3137 self.assertTrue(s.endswith('>'))
3138
3139 def test_dict_arg(self):
3140 h = RecordingHandler()
3141 r = logging.getLogger()
3142 r.addHandler(h)
3143 d = {'less' : 'more' }
3144 logging.warning('less is %(less)s', d)
3145 self.assertIs(h.records[0].args, d)
3146 self.assertEqual(h.records[0].message, 'less is more')
3147 r.removeHandler(h)
3148 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003149
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003150 def test_multiprocessing(self):
3151 r = logging.makeLogRecord({})
3152 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003153 try:
3154 import multiprocessing as mp
3155 r = logging.makeLogRecord({})
3156 self.assertEqual(r.processName, mp.current_process().name)
3157 except ImportError:
3158 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003159
3160 def test_optional(self):
3161 r = logging.makeLogRecord({})
3162 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003163 if threading:
3164 NOT_NONE(r.thread)
3165 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003166 NOT_NONE(r.process)
3167 NOT_NONE(r.processName)
3168 log_threads = logging.logThreads
3169 log_processes = logging.logProcesses
3170 log_multiprocessing = logging.logMultiprocessing
3171 try:
3172 logging.logThreads = False
3173 logging.logProcesses = False
3174 logging.logMultiprocessing = False
3175 r = logging.makeLogRecord({})
3176 NONE = self.assertIsNone
3177 NONE(r.thread)
3178 NONE(r.threadName)
3179 NONE(r.process)
3180 NONE(r.processName)
3181 finally:
3182 logging.logThreads = log_threads
3183 logging.logProcesses = log_processes
3184 logging.logMultiprocessing = log_multiprocessing
3185
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003186class BasicConfigTest(unittest.TestCase):
3187
Vinay Sajip95bf5042011-04-20 11:50:56 +01003188 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003189
3190 def setUp(self):
3191 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003192 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003193 self.saved_handlers = logging._handlers.copy()
3194 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003195 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003196 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003197 logging.root.handlers = []
3198
3199 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003200 for h in logging.root.handlers[:]:
3201 logging.root.removeHandler(h)
3202 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003203 super(BasicConfigTest, self).tearDown()
3204
Vinay Sajip3def7e02011-04-20 10:58:06 +01003205 def cleanup(self):
3206 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003207 logging._handlers.clear()
3208 logging._handlers.update(self.saved_handlers)
3209 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003210 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003211
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003212 def test_no_kwargs(self):
3213 logging.basicConfig()
3214
3215 # handler defaults to a StreamHandler to sys.stderr
3216 self.assertEqual(len(logging.root.handlers), 1)
3217 handler = logging.root.handlers[0]
3218 self.assertIsInstance(handler, logging.StreamHandler)
3219 self.assertEqual(handler.stream, sys.stderr)
3220
3221 formatter = handler.formatter
3222 # format defaults to logging.BASIC_FORMAT
3223 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3224 # datefmt defaults to None
3225 self.assertIsNone(formatter.datefmt)
3226 # style defaults to %
3227 self.assertIsInstance(formatter._style, logging.PercentStyle)
3228
Florent Xiclunae41f0de2011-11-11 19:39:25 +01003229 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003230 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003231
3232 def test_filename(self):
3233 logging.basicConfig(filename='test.log')
3234
3235 self.assertEqual(len(logging.root.handlers), 1)
3236 handler = logging.root.handlers[0]
3237 self.assertIsInstance(handler, logging.FileHandler)
3238
3239 expected = logging.FileHandler('test.log', 'a')
3240 self.addCleanup(expected.close)
3241 self.assertEqual(handler.stream.mode, expected.stream.mode)
3242 self.assertEqual(handler.stream.name, expected.stream.name)
3243
3244 def test_filemode(self):
3245 logging.basicConfig(filename='test.log', filemode='wb')
3246
3247 handler = logging.root.handlers[0]
3248 expected = logging.FileHandler('test.log', 'wb')
3249 self.addCleanup(expected.close)
3250 self.assertEqual(handler.stream.mode, expected.stream.mode)
3251
3252 def test_stream(self):
3253 stream = io.StringIO()
3254 self.addCleanup(stream.close)
3255 logging.basicConfig(stream=stream)
3256
3257 self.assertEqual(len(logging.root.handlers), 1)
3258 handler = logging.root.handlers[0]
3259 self.assertIsInstance(handler, logging.StreamHandler)
3260 self.assertEqual(handler.stream, stream)
3261
3262 def test_format(self):
3263 logging.basicConfig(format='foo')
3264
3265 formatter = logging.root.handlers[0].formatter
3266 self.assertEqual(formatter._style._fmt, 'foo')
3267
3268 def test_datefmt(self):
3269 logging.basicConfig(datefmt='bar')
3270
3271 formatter = logging.root.handlers[0].formatter
3272 self.assertEqual(formatter.datefmt, 'bar')
3273
3274 def test_style(self):
3275 logging.basicConfig(style='$')
3276
3277 formatter = logging.root.handlers[0].formatter
3278 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3279
3280 def test_level(self):
3281 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003282 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003283
3284 logging.basicConfig(level=57)
3285 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003286 # Test that second call has no effect
3287 logging.basicConfig(level=58)
3288 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003289
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003290 def test_incompatible(self):
3291 assertRaises = self.assertRaises
3292 handlers = [logging.StreamHandler()]
3293 stream = sys.stderr
3294 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3295 stream=stream)
3296 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3297 handlers=handlers)
3298 assertRaises(ValueError, logging.basicConfig, stream=stream,
3299 handlers=handlers)
3300
3301 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003302 handlers = [
3303 logging.StreamHandler(),
3304 logging.StreamHandler(sys.stdout),
3305 logging.StreamHandler(),
3306 ]
3307 f = logging.Formatter()
3308 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003309 logging.basicConfig(handlers=handlers)
3310 self.assertIs(handlers[0], logging.root.handlers[0])
3311 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003312 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003313 self.assertIsNotNone(handlers[0].formatter)
3314 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003315 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003316 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3317
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003318 def _test_log(self, method, level=None):
3319 # logging.root has no handlers so basicConfig should be called
3320 called = []
3321
3322 old_basic_config = logging.basicConfig
3323 def my_basic_config(*a, **kw):
3324 old_basic_config()
3325 old_level = logging.root.level
3326 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003327 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003328 called.append((a, kw))
3329
3330 patch(self, logging, 'basicConfig', my_basic_config)
3331
3332 log_method = getattr(logging, method)
3333 if level is not None:
3334 log_method(level, "test me")
3335 else:
3336 log_method("test me")
3337
3338 # basicConfig was called with no arguments
3339 self.assertEqual(called, [((), {})])
3340
3341 def test_log(self):
3342 self._test_log('log', logging.WARNING)
3343
3344 def test_debug(self):
3345 self._test_log('debug')
3346
3347 def test_info(self):
3348 self._test_log('info')
3349
3350 def test_warning(self):
3351 self._test_log('warning')
3352
3353 def test_error(self):
3354 self._test_log('error')
3355
3356 def test_critical(self):
3357 self._test_log('critical')
3358
3359
3360class LoggerAdapterTest(unittest.TestCase):
3361
3362 def setUp(self):
3363 super(LoggerAdapterTest, self).setUp()
3364 old_handler_list = logging._handlerList[:]
3365
3366 self.recording = RecordingHandler()
3367 self.logger = logging.root
3368 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003369 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003370 self.addCleanup(self.recording.close)
3371
3372 def cleanup():
3373 logging._handlerList[:] = old_handler_list
3374
3375 self.addCleanup(cleanup)
3376 self.addCleanup(logging.shutdown)
3377 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3378
3379 def test_exception(self):
3380 msg = 'testing exception: %r'
3381 exc = None
3382 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003383 1 / 0
3384 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003385 exc = e
3386 self.adapter.exception(msg, self.recording)
3387
3388 self.assertEqual(len(self.recording.records), 1)
3389 record = self.recording.records[0]
3390 self.assertEqual(record.levelno, logging.ERROR)
3391 self.assertEqual(record.msg, msg)
3392 self.assertEqual(record.args, (self.recording,))
3393 self.assertEqual(record.exc_info,
3394 (exc.__class__, exc, exc.__traceback__))
3395
3396 def test_critical(self):
3397 msg = 'critical test! %r'
3398 self.adapter.critical(msg, self.recording)
3399
3400 self.assertEqual(len(self.recording.records), 1)
3401 record = self.recording.records[0]
3402 self.assertEqual(record.levelno, logging.CRITICAL)
3403 self.assertEqual(record.msg, msg)
3404 self.assertEqual(record.args, (self.recording,))
3405
3406 def test_is_enabled_for(self):
3407 old_disable = self.adapter.logger.manager.disable
3408 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003409 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3410 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003411 self.assertFalse(self.adapter.isEnabledFor(32))
3412
3413 def test_has_handlers(self):
3414 self.assertTrue(self.adapter.hasHandlers())
3415
3416 for handler in self.logger.handlers:
3417 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003418
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003419 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003420 self.assertFalse(self.adapter.hasHandlers())
3421
3422
3423class LoggerTest(BaseTest):
3424
3425 def setUp(self):
3426 super(LoggerTest, self).setUp()
3427 self.recording = RecordingHandler()
3428 self.logger = logging.Logger(name='blah')
3429 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003430 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003431 self.addCleanup(self.recording.close)
3432 self.addCleanup(logging.shutdown)
3433
3434 def test_set_invalid_level(self):
3435 self.assertRaises(TypeError, self.logger.setLevel, object())
3436
3437 def test_exception(self):
3438 msg = 'testing exception: %r'
3439 exc = None
3440 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003441 1 / 0
3442 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003443 exc = e
3444 self.logger.exception(msg, self.recording)
3445
3446 self.assertEqual(len(self.recording.records), 1)
3447 record = self.recording.records[0]
3448 self.assertEqual(record.levelno, logging.ERROR)
3449 self.assertEqual(record.msg, msg)
3450 self.assertEqual(record.args, (self.recording,))
3451 self.assertEqual(record.exc_info,
3452 (exc.__class__, exc, exc.__traceback__))
3453
3454 def test_log_invalid_level_with_raise(self):
3455 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003456 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003457
3458 logging.raiseExceptions = True
3459 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3460
3461 def test_log_invalid_level_no_raise(self):
3462 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003463 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003464
3465 logging.raiseExceptions = False
3466 self.logger.log('10', 'test message') # no exception happens
3467
3468 def test_find_caller_with_stack_info(self):
3469 called = []
3470 patch(self, logging.traceback, 'print_stack',
3471 lambda f, file: called.append(file.getvalue()))
3472
3473 self.logger.findCaller(stack_info=True)
3474
3475 self.assertEqual(len(called), 1)
3476 self.assertEqual('Stack (most recent call last):\n', called[0])
3477
3478 def test_make_record_with_extra_overwrite(self):
3479 name = 'my record'
3480 level = 13
3481 fn = lno = msg = args = exc_info = func = sinfo = None
3482 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3483 exc_info, func, sinfo)
3484
3485 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3486 extra = {key: 'some value'}
3487 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3488 fn, lno, msg, args, exc_info,
3489 extra=extra, sinfo=sinfo)
3490
3491 def test_make_record_with_extra_no_overwrite(self):
3492 name = 'my record'
3493 level = 13
3494 fn = lno = msg = args = exc_info = func = sinfo = None
3495 extra = {'valid_key': 'some value'}
3496 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3497 exc_info, extra=extra, sinfo=sinfo)
3498 self.assertIn('valid_key', result.__dict__)
3499
3500 def test_has_handlers(self):
3501 self.assertTrue(self.logger.hasHandlers())
3502
3503 for handler in self.logger.handlers:
3504 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003505 self.assertFalse(self.logger.hasHandlers())
3506
3507 def test_has_handlers_no_propagate(self):
3508 child_logger = logging.getLogger('blah.child')
3509 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003510 self.assertFalse(child_logger.hasHandlers())
3511
3512 def test_is_enabled_for(self):
3513 old_disable = self.logger.manager.disable
3514 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003515 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003516 self.assertFalse(self.logger.isEnabledFor(22))
3517
Florent Xicluna5252f9f2011-11-07 19:43:05 +01003518 def test_root_logger_aliases(self):
3519 root = logging.getLogger()
3520 self.assertIs(root, logging.root)
3521 self.assertIs(root, logging.getLogger(None))
3522 self.assertIs(root, logging.getLogger(''))
3523 self.assertIs(root, logging.getLogger('foo').root)
3524 self.assertIs(root, logging.getLogger('foo.bar').root)
3525 self.assertIs(root, logging.getLogger('foo').parent)
3526
3527 self.assertIsNot(root, logging.getLogger('\0'))
3528 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
3529
3530 def test_invalid_names(self):
3531 self.assertRaises(TypeError, logging.getLogger, any)
3532 self.assertRaises(TypeError, logging.getLogger, b'foo')
3533
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003534
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003535class BaseFileTest(BaseTest):
3536 "Base class for handler tests that write log files"
3537
3538 def setUp(self):
3539 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003540 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3541 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003542 self.rmfiles = []
3543
3544 def tearDown(self):
3545 for fn in self.rmfiles:
3546 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003547 if os.path.exists(self.fn):
3548 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003549 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003550
3551 def assertLogFile(self, filename):
3552 "Assert a log file is there and register it for deletion"
3553 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003554 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003555 self.rmfiles.append(filename)
3556
3557
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003558class FileHandlerTest(BaseFileTest):
3559 def test_delay(self):
3560 os.unlink(self.fn)
3561 fh = logging.FileHandler(self.fn, delay=True)
3562 self.assertIsNone(fh.stream)
3563 self.assertFalse(os.path.exists(self.fn))
3564 fh.handle(logging.makeLogRecord({}))
3565 self.assertIsNotNone(fh.stream)
3566 self.assertTrue(os.path.exists(self.fn))
3567 fh.close()
3568
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003569class RotatingFileHandlerTest(BaseFileTest):
3570 def next_rec(self):
3571 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3572 self.next_message(), None, None, None)
3573
3574 def test_should_not_rollover(self):
3575 # If maxbytes is zero rollover never occurs
3576 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3577 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003578 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003579
3580 def test_should_rollover(self):
3581 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3582 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003583 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003584
3585 def test_file_created(self):
3586 # checks that the file is created and assumes it was created
3587 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003588 rh = logging.handlers.RotatingFileHandler(self.fn)
3589 rh.emit(self.next_rec())
3590 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003591 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003592
3593 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00003594 def namer(name):
3595 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003596 rh = logging.handlers.RotatingFileHandler(
3597 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003598 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003599 rh.emit(self.next_rec())
3600 self.assertLogFile(self.fn)
3601 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003602 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003603 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003604 self.assertLogFile(namer(self.fn + ".2"))
3605 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
3606 rh.close()
3607
Eric V. Smith851cad72012-03-11 22:46:04 -07003608 @requires_zlib
Vinay Sajip23b94d02012-01-04 12:02:26 +00003609 def test_rotator(self):
3610 def namer(name):
3611 return name + ".gz"
3612
3613 def rotator(source, dest):
3614 with open(source, "rb") as sf:
3615 data = sf.read()
3616 compressed = zlib.compress(data, 9)
3617 with open(dest, "wb") as df:
3618 df.write(compressed)
3619 os.remove(source)
3620
3621 rh = logging.handlers.RotatingFileHandler(
3622 self.fn, backupCount=2, maxBytes=1)
3623 rh.rotator = rotator
3624 rh.namer = namer
3625 m1 = self.next_rec()
3626 rh.emit(m1)
3627 self.assertLogFile(self.fn)
3628 m2 = self.next_rec()
3629 rh.emit(m2)
3630 fn = namer(self.fn + ".1")
3631 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003632 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00003633 with open(fn, "rb") as f:
3634 compressed = f.read()
3635 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003636 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003637 rh.emit(self.next_rec())
3638 fn = namer(self.fn + ".2")
3639 self.assertLogFile(fn)
3640 with open(fn, "rb") as f:
3641 compressed = f.read()
3642 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003643 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003644 rh.emit(self.next_rec())
3645 fn = namer(self.fn + ".2")
3646 with open(fn, "rb") as f:
3647 compressed = f.read()
3648 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003649 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003650 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00003651 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003652
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003653class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003654 # other test methods added below
3655 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003656 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3657 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00003658 fmt = logging.Formatter('%(asctime)s %(message)s')
3659 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003660 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003661 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003662 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003663 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00003664 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003665 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01003666 fh.close()
3667 # At this point, we should have a recent rotated file which we
3668 # can test for the existence of. However, in practice, on some
3669 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003670 # in time to go to look for the log file. So, we go back a fair
3671 # bit, and stop as soon as we see a rotated file. In theory this
3672 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003673 found = False
3674 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003675 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003676 for secs in range(GO_BACK):
3677 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003678 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3679 found = os.path.exists(fn)
3680 if found:
3681 self.rmfiles.append(fn)
3682 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003683 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3684 if not found:
3685 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003686 dn, fn = os.path.split(self.fn)
3687 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003688 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3689 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00003690 for f in files:
3691 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00003692 path = os.path.join(dn, f)
3693 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00003694 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01003695 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003696
Vinay Sajip0372e102011-05-05 12:59:14 +01003697 def test_invalid(self):
3698 assertRaises = self.assertRaises
3699 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003700 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003701 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003702 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003703 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003704 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003705
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003706def secs(**kw):
3707 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3708
3709for when, exp in (('S', 1),
3710 ('M', 60),
3711 ('H', 60 * 60),
3712 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003713 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003714 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003715 ('W0', secs(days=4, hours=24)),
3716 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003717 def test_compute_rollover(self, when=when, exp=exp):
3718 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003719 self.fn, when=when, interval=1, backupCount=0, utc=True)
3720 currentTime = 0.0
3721 actual = rh.computeRollover(currentTime)
3722 if exp != actual:
3723 # Failures occur on some systems for MIDNIGHT and W0.
3724 # Print detailed calculation for MIDNIGHT so we can try to see
3725 # what's going on
3726 import time
3727 if when == 'MIDNIGHT':
3728 try:
3729 if rh.utc:
3730 t = time.gmtime(currentTime)
3731 else:
3732 t = time.localtime(currentTime)
3733 currentHour = t[3]
3734 currentMinute = t[4]
3735 currentSecond = t[5]
3736 # r is the number of seconds left between now and midnight
3737 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3738 currentMinute) * 60 +
3739 currentSecond)
3740 result = currentTime + r
3741 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3742 print('currentHour: %s' % currentHour, file=sys.stderr)
3743 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3744 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3745 print('r: %s' % r, file=sys.stderr)
3746 print('result: %s' % result, file=sys.stderr)
3747 except Exception:
3748 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3749 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003750 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003751 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3752
Vinay Sajip60ccd822011-05-09 17:32:09 +01003753
3754@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3755class NTEventLogHandlerTest(BaseTest):
3756 def test_basic(self):
3757 logtype = 'Application'
3758 elh = win32evtlog.OpenEventLog(None, logtype)
3759 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3760 h = logging.handlers.NTEventLogHandler('test_logging')
3761 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3762 h.handle(r)
3763 h.close()
3764 # Now see if the event is recorded
3765 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3766 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3767 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3768 found = False
3769 GO_BACK = 100
3770 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3771 for e in events:
3772 if e.SourceName != 'test_logging':
3773 continue
3774 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3775 if msg != 'Test Log Message\r\n':
3776 continue
3777 found = True
3778 break
3779 msg = 'Record not found in event log, went back %d records' % GO_BACK
3780 self.assertTrue(found, msg=msg)
3781
Christian Heimes180510d2008-03-03 19:15:45 +00003782# Set the locale to the platform-dependent default. I have no idea
3783# why the test does this, but in any case we save the current locale
3784# first and restore it at the end.
3785@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003786def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003787 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003788 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003789 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3790 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3791 ManagerTest, FormatterTest, BufferingFormatterTest,
3792 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3793 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3794 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3795 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003796 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003797 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003798 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003799 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003800
Christian Heimes180510d2008-03-03 19:15:45 +00003801if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003802 test_main()