blob: 26baf11b69d40f38367a2d69c0bbaf5a1fd5445c [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajip66b8b082012-04-24 23:25:30 +01003# Copyright 2001-2012 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 Sajip66b8b082012-04-24 23:25:30 +010021Copyright (C) 2001-2012 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
Vinay Sajip66b8b082012-04-24 23:25:30 +010036import random
Christian Heimes180510d2008-03-03 19:15:45 +000037import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000038import select
Christian Heimes180510d2008-03-03 19:15:45 +000039import socket
Christian Heimes180510d2008-03-03 19:15:45 +000040import struct
41import sys
42import tempfile
Eric V. Smith851cad72012-03-11 22:46:04 -070043from test.support import (captured_stdout, run_with_locale, run_unittest,
44 patch, requires_zlib, TestHandler, Matcher)
Christian Heimes180510d2008-03-03 19:15:45 +000045import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010046import time
Christian Heimes180510d2008-03-03 19:15:45 +000047import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000048import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000049import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000050try:
51 import threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010052 # The following imports are needed only for tests which
Florent Xicluna5252f9f2011-11-07 19:43:05 +010053 # require threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010054 import asynchat
55 import asyncore
56 import errno
57 from http.server import HTTPServer, BaseHTTPRequestHandler
58 import smtpd
59 from urllib.parse import urlparse, parse_qs
60 from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
61 ThreadingTCPServer, StreamRequestHandler)
Victor Stinner45df8202010-04-28 22:31:17 +000062except ImportError:
63 threading = None
Vinay Sajip60ccd822011-05-09 17:32:09 +010064try:
65 import win32evtlog
66except ImportError:
67 win32evtlog = None
68try:
69 import win32evtlogutil
70except ImportError:
71 win32evtlogutil = None
72 win32evtlog = None
Eric V. Smith851cad72012-03-11 22:46:04 -070073try:
74 import zlib
75except ImportError:
76 pass
Christian Heimes18c66892008-02-17 13:31:39 +000077
78
Christian Heimes180510d2008-03-03 19:15:45 +000079class BaseTest(unittest.TestCase):
80
81 """Base class for logging tests."""
82
83 log_format = "%(name)s -> %(levelname)s: %(message)s"
84 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
85 message_num = 0
86
87 def setUp(self):
88 """Setup the default logging stream to an internal StringIO instance,
89 so that we can examine log output as we want."""
90 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000091 logging._acquireLock()
92 try:
Christian Heimes180510d2008-03-03 19:15:45 +000093 self.saved_handlers = logging._handlers.copy()
94 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000095 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000096 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000097 self.logger_states = logger_states = {}
98 for name in saved_loggers:
99 logger_states[name] = getattr(saved_loggers[name],
100 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +0000101 finally:
102 logging._releaseLock()
103
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100104 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +0000105 self.logger1 = logging.getLogger("\xab\xd7\xbb")
106 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000107
Christian Heimes180510d2008-03-03 19:15:45 +0000108 self.root_logger = logging.getLogger("")
109 self.original_logging_level = self.root_logger.getEffectiveLevel()
110
111 self.stream = io.StringIO()
112 self.root_logger.setLevel(logging.DEBUG)
113 self.root_hdlr = logging.StreamHandler(self.stream)
114 self.root_formatter = logging.Formatter(self.log_format)
115 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000116 if self.logger1.hasHandlers():
117 hlist = self.logger1.handlers + self.root_logger.handlers
118 raise AssertionError('Unexpected handlers: %s' % hlist)
119 if self.logger2.hasHandlers():
120 hlist = self.logger2.handlers + self.root_logger.handlers
121 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000122 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000123 self.assertTrue(self.logger1.hasHandlers())
124 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000125
126 def tearDown(self):
127 """Remove our logging stream, and restore the original logging
128 level."""
129 self.stream.close()
130 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000131 while self.root_logger.handlers:
132 h = self.root_logger.handlers[0]
133 self.root_logger.removeHandler(h)
134 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000135 self.root_logger.setLevel(self.original_logging_level)
136 logging._acquireLock()
137 try:
138 logging._levelNames.clear()
139 logging._levelNames.update(self.saved_level_names)
140 logging._handlers.clear()
141 logging._handlers.update(self.saved_handlers)
142 logging._handlerList[:] = self.saved_handler_list
143 loggerDict = logging.getLogger().manager.loggerDict
144 loggerDict.clear()
145 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000146 logger_states = self.logger_states
147 for name in self.logger_states:
148 if logger_states[name] is not None:
149 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000150 finally:
151 logging._releaseLock()
152
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000153 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000154 """Match the collected log lines against the regular expression
155 self.expected_log_pat, and compare the extracted group values to
156 the expected_values list of tuples."""
157 stream = stream or self.stream
158 pat = re.compile(self.expected_log_pat)
159 try:
160 stream.reset()
161 actual_lines = stream.readlines()
162 except AttributeError:
163 # StringIO.StringIO lacks a reset() method.
164 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100165 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000166 for actual, expected in zip(actual_lines, expected_values):
167 match = pat.search(actual)
168 if not match:
169 self.fail("Log line does not match expected pattern:\n" +
170 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000171 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000172 s = stream.read()
173 if s:
174 self.fail("Remaining output at end of log stream:\n" + s)
175
176 def next_message(self):
177 """Generate a message consisting solely of an auto-incrementing
178 integer."""
179 self.message_num += 1
180 return "%d" % self.message_num
181
182
183class BuiltinLevelsTest(BaseTest):
184 """Test builtin levels and their inheritance."""
185
186 def test_flat(self):
187 #Logging levels in a flat logger namespace.
188 m = self.next_message
189
190 ERR = logging.getLogger("ERR")
191 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000192 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000193 INF.setLevel(logging.INFO)
194 DEB = logging.getLogger("DEB")
195 DEB.setLevel(logging.DEBUG)
196
197 # These should log.
198 ERR.log(logging.CRITICAL, m())
199 ERR.error(m())
200
201 INF.log(logging.CRITICAL, m())
202 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100203 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000204 INF.info(m())
205
206 DEB.log(logging.CRITICAL, m())
207 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100208 DEB.warning(m())
209 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000210 DEB.debug(m())
211
212 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100213 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000214 ERR.info(m())
215 ERR.debug(m())
216
217 INF.debug(m())
218
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000219 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000220 ('ERR', 'CRITICAL', '1'),
221 ('ERR', 'ERROR', '2'),
222 ('INF', 'CRITICAL', '3'),
223 ('INF', 'ERROR', '4'),
224 ('INF', 'WARNING', '5'),
225 ('INF', 'INFO', '6'),
226 ('DEB', 'CRITICAL', '7'),
227 ('DEB', 'ERROR', '8'),
228 ('DEB', 'WARNING', '9'),
229 ('DEB', 'INFO', '10'),
230 ('DEB', 'DEBUG', '11'),
231 ])
232
233 def test_nested_explicit(self):
234 # Logging levels in a nested namespace, all explicitly set.
235 m = self.next_message
236
237 INF = logging.getLogger("INF")
238 INF.setLevel(logging.INFO)
239 INF_ERR = logging.getLogger("INF.ERR")
240 INF_ERR.setLevel(logging.ERROR)
241
242 # These should log.
243 INF_ERR.log(logging.CRITICAL, m())
244 INF_ERR.error(m())
245
246 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100247 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000248 INF_ERR.info(m())
249 INF_ERR.debug(m())
250
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000251 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000252 ('INF.ERR', 'CRITICAL', '1'),
253 ('INF.ERR', 'ERROR', '2'),
254 ])
255
256 def test_nested_inherited(self):
257 #Logging levels in a nested namespace, inherited from parent loggers.
258 m = self.next_message
259
260 INF = logging.getLogger("INF")
261 INF.setLevel(logging.INFO)
262 INF_ERR = logging.getLogger("INF.ERR")
263 INF_ERR.setLevel(logging.ERROR)
264 INF_UNDEF = logging.getLogger("INF.UNDEF")
265 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
266 UNDEF = logging.getLogger("UNDEF")
267
268 # These should log.
269 INF_UNDEF.log(logging.CRITICAL, m())
270 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100271 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000272 INF_UNDEF.info(m())
273 INF_ERR_UNDEF.log(logging.CRITICAL, m())
274 INF_ERR_UNDEF.error(m())
275
276 # These should not log.
277 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100278 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000279 INF_ERR_UNDEF.info(m())
280 INF_ERR_UNDEF.debug(m())
281
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000282 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000283 ('INF.UNDEF', 'CRITICAL', '1'),
284 ('INF.UNDEF', 'ERROR', '2'),
285 ('INF.UNDEF', 'WARNING', '3'),
286 ('INF.UNDEF', 'INFO', '4'),
287 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
288 ('INF.ERR.UNDEF', 'ERROR', '6'),
289 ])
290
291 def test_nested_with_virtual_parent(self):
292 # Logging levels when some parent does not exist yet.
293 m = self.next_message
294
295 INF = logging.getLogger("INF")
296 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
297 CHILD = logging.getLogger("INF.BADPARENT")
298 INF.setLevel(logging.INFO)
299
300 # These should log.
301 GRANDCHILD.log(logging.FATAL, m())
302 GRANDCHILD.info(m())
303 CHILD.log(logging.FATAL, m())
304 CHILD.info(m())
305
306 # These should not log.
307 GRANDCHILD.debug(m())
308 CHILD.debug(m())
309
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000310 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000311 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
312 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
313 ('INF.BADPARENT', 'CRITICAL', '3'),
314 ('INF.BADPARENT', 'INFO', '4'),
315 ])
316
317
318class BasicFilterTest(BaseTest):
319
320 """Test the bundled Filter class."""
321
322 def test_filter(self):
323 # Only messages satisfying the specified criteria pass through the
324 # filter.
325 filter_ = logging.Filter("spam.eggs")
326 handler = self.root_logger.handlers[0]
327 try:
328 handler.addFilter(filter_)
329 spam = logging.getLogger("spam")
330 spam_eggs = logging.getLogger("spam.eggs")
331 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
332 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
333
334 spam.info(self.next_message())
335 spam_eggs.info(self.next_message()) # Good.
336 spam_eggs_fish.info(self.next_message()) # Good.
337 spam_bakedbeans.info(self.next_message())
338
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000339 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000340 ('spam.eggs', 'INFO', '2'),
341 ('spam.eggs.fish', 'INFO', '3'),
342 ])
343 finally:
344 handler.removeFilter(filter_)
345
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000346 def test_callable_filter(self):
347 # Only messages satisfying the specified criteria pass through the
348 # filter.
349
350 def filterfunc(record):
351 parts = record.name.split('.')
352 prefix = '.'.join(parts[:2])
353 return prefix == 'spam.eggs'
354
355 handler = self.root_logger.handlers[0]
356 try:
357 handler.addFilter(filterfunc)
358 spam = logging.getLogger("spam")
359 spam_eggs = logging.getLogger("spam.eggs")
360 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
361 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
362
363 spam.info(self.next_message())
364 spam_eggs.info(self.next_message()) # Good.
365 spam_eggs_fish.info(self.next_message()) # Good.
366 spam_bakedbeans.info(self.next_message())
367
368 self.assert_log_lines([
369 ('spam.eggs', 'INFO', '2'),
370 ('spam.eggs.fish', 'INFO', '3'),
371 ])
372 finally:
373 handler.removeFilter(filterfunc)
374
Vinay Sajip985ef872011-04-26 19:34:04 +0100375 def test_empty_filter(self):
376 f = logging.Filter()
377 r = logging.makeLogRecord({'name': 'spam.eggs'})
378 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000379
380#
381# First, we define our levels. There can be as many as you want - the only
382# limitations are that they should be integers, the lowest should be > 0 and
383# larger values mean less information being logged. If you need specific
384# level values which do not fit into these limitations, you can use a
385# mapping dictionary to convert between your application levels and the
386# logging system.
387#
388SILENT = 120
389TACITURN = 119
390TERSE = 118
391EFFUSIVE = 117
392SOCIABLE = 116
393VERBOSE = 115
394TALKATIVE = 114
395GARRULOUS = 113
396CHATTERBOX = 112
397BORING = 111
398
399LEVEL_RANGE = range(BORING, SILENT + 1)
400
401#
402# Next, we define names for our levels. You don't need to do this - in which
403# case the system will use "Level n" to denote the text for the level.
404#
405my_logging_levels = {
406 SILENT : 'Silent',
407 TACITURN : 'Taciturn',
408 TERSE : 'Terse',
409 EFFUSIVE : 'Effusive',
410 SOCIABLE : 'Sociable',
411 VERBOSE : 'Verbose',
412 TALKATIVE : 'Talkative',
413 GARRULOUS : 'Garrulous',
414 CHATTERBOX : 'Chatterbox',
415 BORING : 'Boring',
416}
417
418class GarrulousFilter(logging.Filter):
419
420 """A filter which blocks garrulous messages."""
421
422 def filter(self, record):
423 return record.levelno != GARRULOUS
424
425class VerySpecificFilter(logging.Filter):
426
427 """A filter which blocks sociable and taciturn messages."""
428
429 def filter(self, record):
430 return record.levelno not in [SOCIABLE, TACITURN]
431
432
433class CustomLevelsAndFiltersTest(BaseTest):
434
435 """Test various filtering possibilities with custom logging levels."""
436
437 # Skip the logger name group.
438 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
439
440 def setUp(self):
441 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000442 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000443 logging.addLevelName(k, v)
444
445 def log_at_all_levels(self, logger):
446 for lvl in LEVEL_RANGE:
447 logger.log(lvl, self.next_message())
448
449 def test_logger_filter(self):
450 # Filter at logger level.
451 self.root_logger.setLevel(VERBOSE)
452 # Levels >= 'Verbose' are good.
453 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000454 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000455 ('Verbose', '5'),
456 ('Sociable', '6'),
457 ('Effusive', '7'),
458 ('Terse', '8'),
459 ('Taciturn', '9'),
460 ('Silent', '10'),
461 ])
462
463 def test_handler_filter(self):
464 # Filter at handler level.
465 self.root_logger.handlers[0].setLevel(SOCIABLE)
466 try:
467 # Levels >= 'Sociable' are good.
468 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000469 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000470 ('Sociable', '6'),
471 ('Effusive', '7'),
472 ('Terse', '8'),
473 ('Taciturn', '9'),
474 ('Silent', '10'),
475 ])
476 finally:
477 self.root_logger.handlers[0].setLevel(logging.NOTSET)
478
479 def test_specific_filters(self):
480 # Set a specific filter object on the handler, and then add another
481 # filter object on the logger itself.
482 handler = self.root_logger.handlers[0]
483 specific_filter = None
484 garr = GarrulousFilter()
485 handler.addFilter(garr)
486 try:
487 self.log_at_all_levels(self.root_logger)
488 first_lines = [
489 # Notice how 'Garrulous' is missing
490 ('Boring', '1'),
491 ('Chatterbox', '2'),
492 ('Talkative', '4'),
493 ('Verbose', '5'),
494 ('Sociable', '6'),
495 ('Effusive', '7'),
496 ('Terse', '8'),
497 ('Taciturn', '9'),
498 ('Silent', '10'),
499 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000500 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000501
502 specific_filter = VerySpecificFilter()
503 self.root_logger.addFilter(specific_filter)
504 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000505 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000506 # Not only 'Garrulous' is still missing, but also 'Sociable'
507 # and 'Taciturn'
508 ('Boring', '11'),
509 ('Chatterbox', '12'),
510 ('Talkative', '14'),
511 ('Verbose', '15'),
512 ('Effusive', '17'),
513 ('Terse', '18'),
514 ('Silent', '20'),
515 ])
516 finally:
517 if specific_filter:
518 self.root_logger.removeFilter(specific_filter)
519 handler.removeFilter(garr)
520
521
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100522class HandlerTest(BaseTest):
523 def test_name(self):
524 h = logging.Handler()
525 h.name = 'generic'
526 self.assertEqual(h.name, 'generic')
527 h.name = 'anothergeneric'
528 self.assertEqual(h.name, 'anothergeneric')
529 self.assertRaises(NotImplementedError, h.emit, None)
530
Vinay Sajip5a35b062011-04-27 11:31:14 +0100531 def test_builtin_handlers(self):
532 # We can't actually *use* too many handlers in the tests,
533 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200534 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100535 for existing in (True, False):
536 fd, fn = tempfile.mkstemp()
537 os.close(fd)
538 if not existing:
539 os.unlink(fn)
540 h = logging.handlers.WatchedFileHandler(fn, delay=True)
541 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100542 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100543 self.assertEqual(dev, -1)
544 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100545 r = logging.makeLogRecord({'msg': 'Test'})
546 h.handle(r)
547 # Now remove the file.
548 os.unlink(fn)
549 self.assertFalse(os.path.exists(fn))
550 # The next call should recreate the file.
551 h.handle(r)
552 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100553 else:
554 self.assertEqual(h.dev, -1)
555 self.assertEqual(h.ino, -1)
556 h.close()
557 if existing:
558 os.unlink(fn)
559 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100560 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100561 else:
562 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100563 try:
564 h = logging.handlers.SysLogHandler(sockname)
565 self.assertEqual(h.facility, h.LOG_USER)
566 self.assertTrue(h.unixsocket)
567 h.close()
568 except socket.error: # syslogd might not be available
569 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100570 for method in ('GET', 'POST', 'PUT'):
571 if method == 'PUT':
572 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
573 'localhost', '/log', method)
574 else:
575 h = logging.handlers.HTTPHandler('localhost', '/log', method)
576 h.close()
577 h = logging.handlers.BufferingHandler(0)
578 r = logging.makeLogRecord({})
579 self.assertTrue(h.shouldFlush(r))
580 h.close()
581 h = logging.handlers.BufferingHandler(1)
582 self.assertFalse(h.shouldFlush(r))
583 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100584
Vinay Sajipa5798de2012-04-24 23:33:33 +0100585 @unittest.skipUnless(threading, 'Threading required for this test.')
586 def test_race(self):
587 # Issue #14632 refers.
588 def remove_loop(fname, tries):
589 for _ in range(tries):
590 try:
591 os.unlink(fname)
592 except OSError:
593 pass
594 time.sleep(0.004 * random.randint(0, 4))
595
Vinay Sajipc94871a2012-04-25 10:51:35 +0100596 del_count = 500
597 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100598
Vinay Sajipa5798de2012-04-24 23:33:33 +0100599 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100600 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
601 os.close(fd)
602 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
603 remover.daemon = True
604 remover.start()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100605 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100606 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
607 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100608 try:
609 for _ in range(log_count):
610 time.sleep(0.005)
611 r = logging.makeLogRecord({'msg': 'testing' })
612 h.handle(r)
613 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100614 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100615 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100616 if os.path.exists(fn):
617 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100618
619
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100620class BadStream(object):
621 def write(self, data):
622 raise RuntimeError('deliberate mistake')
623
624class TestStreamHandler(logging.StreamHandler):
625 def handleError(self, record):
626 self.error_record = record
627
628class StreamHandlerTest(BaseTest):
629 def test_error_handling(self):
630 h = TestStreamHandler(BadStream())
631 r = logging.makeLogRecord({})
632 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100633 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100634 try:
635 h.handle(r)
636 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100637 h = logging.StreamHandler(BadStream())
638 sys.stderr = sio = io.StringIO()
639 h.handle(r)
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100640 self.assertIn('\nRuntimeError: deliberate mistake\n',
641 sio.getvalue())
Vinay Sajip985ef872011-04-26 19:34:04 +0100642 logging.raiseExceptions = False
643 sys.stderr = sio = io.StringIO()
644 h.handle(r)
645 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100646 finally:
647 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100648 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100649
Vinay Sajip7367d082011-05-02 13:17:27 +0100650# -- The following section could be moved into a server_helper.py module
651# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100652
Vinay Sajipce7c9782011-05-17 07:15:53 +0100653if threading:
654 class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100655 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100656 This derived class has had to be created because smtpd does not
657 support use of custom channel maps, although they are allowed by
658 asyncore's design. Issue #11959 has been raised to address this,
659 and if resolved satisfactorily, some of this code can be removed.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100660 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100661 def __init__(self, server, conn, addr, sockmap):
662 asynchat.async_chat.__init__(self, conn, sockmap)
663 self.smtp_server = server
664 self.conn = conn
665 self.addr = addr
R David Murrayd1a30c92012-05-26 14:33:59 -0400666 self.data_size_limit = None
Vinay Sajipce7c9782011-05-17 07:15:53 +0100667 self.received_lines = []
668 self.smtp_state = self.COMMAND
669 self.seen_greeting = ''
670 self.mailfrom = None
671 self.rcpttos = []
672 self.received_data = ''
673 self.fqdn = socket.getfqdn()
674 self.num_bytes = 0
675 try:
676 self.peer = conn.getpeername()
677 except socket.error as err:
678 # a race condition may occur if the other end is closing
679 # before we can get the peername
680 self.close()
681 if err.args[0] != errno.ENOTCONN:
682 raise
683 return
684 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
685 self.set_terminator(b'\r\n')
R David Murrayd1a30c92012-05-26 14:33:59 -0400686 self.extended_smtp = False
Vinay Sajipa463d252011-04-30 21:52:48 +0100687
Vinay Sajip314b92b2011-05-02 14:31:16 +0100688
Vinay Sajipce7c9782011-05-17 07:15:53 +0100689 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100690 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100691 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100692
Vinay Sajipce7c9782011-05-17 07:15:53 +0100693 :param addr: A (host, port) tuple which the server listens on.
694 You can specify a port value of zero: the server's
695 *port* attribute will hold the actual port number
696 used, which can be used in client connections.
697 :param handler: A callable which will be called to process
698 incoming messages. The handler will be passed
699 the client address tuple, who the message is from,
700 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100701 :param poll_interval: The interval, in seconds, used in the underlying
702 :func:`select` or :func:`poll` call by
703 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100704 :param sockmap: A dictionary which will be used to hold
705 :class:`asyncore.dispatcher` instances used by
706 :func:`asyncore.loop`. This avoids changing the
707 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100708 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100709 channel_class = TestSMTPChannel
710
711 def __init__(self, addr, handler, poll_interval, sockmap):
712 self._localaddr = addr
713 self._remoteaddr = None
R David Murrayd1a30c92012-05-26 14:33:59 -0400714 self.data_size_limit = None
Vinay Sajipce7c9782011-05-17 07:15:53 +0100715 self.sockmap = sockmap
716 asyncore.dispatcher.__init__(self, map=sockmap)
717 try:
718 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
719 sock.setblocking(0)
720 self.set_socket(sock, map=sockmap)
721 # try to re-use a server port if possible
722 self.set_reuse_addr()
723 self.bind(addr)
724 self.port = sock.getsockname()[1]
725 self.listen(5)
726 except:
727 self.close()
Vinay Sajipe73afad2011-05-10 07:48:28 +0100728 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +0100729 self._handler = handler
730 self._thread = None
731 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100732
Vinay Sajipce7c9782011-05-17 07:15:53 +0100733 def handle_accepted(self, conn, addr):
734 """
735 Redefined only because the base class does not pass in a
736 map, forcing use of a global in :mod:`asyncore`.
737 """
738 channel = self.channel_class(self, conn, addr, self.sockmap)
Vinay Sajip314b92b2011-05-02 14:31:16 +0100739
Vinay Sajipce7c9782011-05-17 07:15:53 +0100740 def process_message(self, peer, mailfrom, rcpttos, data):
741 """
742 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100743
Vinay Sajipce7c9782011-05-17 07:15:53 +0100744 Typically, this will be a test case method.
745 :param peer: The client (host, port) tuple.
746 :param mailfrom: The address of the sender.
747 :param rcpttos: The addresses of the recipients.
748 :param data: The message.
749 """
750 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100751
Vinay Sajipce7c9782011-05-17 07:15:53 +0100752 def start(self):
753 """
754 Start the server running on a separate daemon thread.
755 """
756 self._thread = t = threading.Thread(target=self.serve_forever,
757 args=(self.poll_interval,))
758 t.setDaemon(True)
759 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100760
Vinay Sajipce7c9782011-05-17 07:15:53 +0100761 def serve_forever(self, poll_interval):
762 """
763 Run the :mod:`asyncore` loop until normal termination
764 conditions arise.
765 :param poll_interval: The interval, in seconds, used in the underlying
766 :func:`select` or :func:`poll` call by
767 :func:`asyncore.loop`.
768 """
769 try:
770 asyncore.loop(poll_interval, map=self.sockmap)
771 except select.error:
772 # On FreeBSD 8, closing the server repeatably
773 # raises this error. We swallow it if the
774 # server has been closed.
775 if self.connected or self.accepting:
776 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100777
Vinay Sajipce7c9782011-05-17 07:15:53 +0100778 def stop(self, timeout=None):
779 """
780 Stop the thread by closing the server instance.
781 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100782
Vinay Sajipce7c9782011-05-17 07:15:53 +0100783 :param timeout: How long to wait for the server thread
784 to terminate.
785 """
786 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100787 self._thread.join(timeout)
788 self._thread = None
Vinay Sajip7367d082011-05-02 13:17:27 +0100789
Vinay Sajipce7c9782011-05-17 07:15:53 +0100790 class ControlMixin(object):
791 """
792 This mixin is used to start a server on a separate thread, and
793 shut it down programmatically. Request handling is simplified - instead
794 of needing to derive a suitable RequestHandler subclass, you just
795 provide a callable which will be passed each received request to be
796 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100797
Vinay Sajipce7c9782011-05-17 07:15:53 +0100798 :param handler: A handler callable which will be called with a
799 single parameter - the request - in order to
800 process the request. This handler is called on the
801 server thread, effectively meaning that requests are
802 processed serially. While not quite Web scale ;-),
803 this should be fine for testing applications.
804 :param poll_interval: The polling interval in seconds.
805 """
806 def __init__(self, handler, poll_interval):
807 self._thread = None
808 self.poll_interval = poll_interval
809 self._handler = handler
810 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100811
Vinay Sajipce7c9782011-05-17 07:15:53 +0100812 def start(self):
813 """
814 Create a daemon thread to run the server, and start it.
815 """
816 self._thread = t = threading.Thread(target=self.serve_forever,
817 args=(self.poll_interval,))
818 t.setDaemon(True)
819 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100820
Vinay Sajipce7c9782011-05-17 07:15:53 +0100821 def serve_forever(self, poll_interval):
822 """
823 Run the server. Set the ready flag before entering the
824 service loop.
825 """
826 self.ready.set()
827 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100828
Vinay Sajipce7c9782011-05-17 07:15:53 +0100829 def stop(self, timeout=None):
830 """
831 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100832
Vinay Sajipce7c9782011-05-17 07:15:53 +0100833 :param timeout: How long to wait for the server thread
834 to terminate.
835 """
836 self.shutdown()
837 if self._thread is not None:
838 self._thread.join(timeout)
839 self._thread = None
840 self.server_close()
841 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100842
Vinay Sajipce7c9782011-05-17 07:15:53 +0100843 class TestHTTPServer(ControlMixin, HTTPServer):
844 """
845 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100846
Vinay Sajipce7c9782011-05-17 07:15:53 +0100847 :param addr: A tuple with the IP address and port to listen on.
848 :param handler: A handler callable which will be called with a
849 single parameter - the request - in order to
850 process the request.
851 :param poll_interval: The polling interval in seconds.
852 :param log: Pass ``True`` to enable log messages.
853 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100854 def __init__(self, addr, handler, poll_interval=0.5,
855 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100856 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
857 def __getattr__(self, name, default=None):
858 if name.startswith('do_'):
859 return self.process_request
860 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100861
Vinay Sajipce7c9782011-05-17 07:15:53 +0100862 def process_request(self):
863 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100864
Vinay Sajipce7c9782011-05-17 07:15:53 +0100865 def log_message(self, format, *args):
866 if log:
867 super(DelegatingHTTPRequestHandler,
868 self).log_message(format, *args)
869 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
870 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100871 self.sslctx = sslctx
872
873 def get_request(self):
874 try:
875 sock, addr = self.socket.accept()
876 if self.sslctx:
877 sock = self.sslctx.wrap_socket(sock, server_side=True)
878 except socket.error as e:
879 # socket errors are silenced by the caller, print them here
880 sys.stderr.write("Got an error:\n%s\n" % e)
881 raise
882 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100883
Vinay Sajipce7c9782011-05-17 07:15:53 +0100884 class TestTCPServer(ControlMixin, ThreadingTCPServer):
885 """
886 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100887
Vinay Sajipce7c9782011-05-17 07:15:53 +0100888 :param addr: A tuple with the IP address and port to listen on.
889 :param handler: A handler callable which will be called with a single
890 parameter - the request - in order to process the request.
891 :param poll_interval: The polling interval in seconds.
892 :bind_and_activate: If True (the default), binds the server and starts it
893 listening. If False, you need to call
894 :meth:`server_bind` and :meth:`server_activate` at
895 some later time before calling :meth:`start`, so that
896 the server will set up the socket and listen on it.
897 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100898
Vinay Sajipce7c9782011-05-17 07:15:53 +0100899 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100900
Vinay Sajipce7c9782011-05-17 07:15:53 +0100901 def __init__(self, addr, handler, poll_interval=0.5,
902 bind_and_activate=True):
903 class DelegatingTCPRequestHandler(StreamRequestHandler):
904
905 def handle(self):
906 self.server._handler(self)
907 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
908 bind_and_activate)
909 ControlMixin.__init__(self, handler, poll_interval)
910
911 def server_bind(self):
912 super(TestTCPServer, self).server_bind()
913 self.port = self.socket.getsockname()[1]
914
915 class TestUDPServer(ControlMixin, ThreadingUDPServer):
916 """
917 A UDP server which is controllable using :class:`ControlMixin`.
918
919 :param addr: A tuple with the IP address and port to listen on.
920 :param handler: A handler callable which will be called with a
921 single parameter - the request - in order to
922 process the request.
923 :param poll_interval: The polling interval for shutdown requests,
924 in seconds.
925 :bind_and_activate: If True (the default), binds the server and
926 starts it listening. If False, you need to
927 call :meth:`server_bind` and
928 :meth:`server_activate` at some later time
929 before calling :meth:`start`, so that the server will
930 set up the socket and listen on it.
931 """
Vinay Sajip3ef12292011-05-23 23:00:42 +0100932 def __init__(self, addr, handler, poll_interval=0.5,
933 bind_and_activate=True):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100934 class DelegatingUDPRequestHandler(DatagramRequestHandler):
935
936 def handle(self):
937 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100938
939 def finish(self):
940 data = self.wfile.getvalue()
Vinay Sajip3ef12292011-05-23 23:00:42 +0100941 if data:
942 try:
943 super(DelegatingUDPRequestHandler, self).finish()
944 except socket.error:
945 if not self.server._closed:
946 raise
Vinay Sajipba980db2011-05-23 21:37:54 +0100947
Vinay Sajip3ef12292011-05-23 23:00:42 +0100948 ThreadingUDPServer.__init__(self, addr,
949 DelegatingUDPRequestHandler,
Vinay Sajipce7c9782011-05-17 07:15:53 +0100950 bind_and_activate)
951 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100952 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100953
954 def server_bind(self):
955 super(TestUDPServer, self).server_bind()
956 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100957
Vinay Sajipba980db2011-05-23 21:37:54 +0100958 def server_close(self):
959 super(TestUDPServer, self).server_close()
960 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100961
962# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100963
Vinay Sajipce7c9782011-05-17 07:15:53 +0100964@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100965class SMTPHandlerTest(BaseTest):
966 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100967 sockmap = {}
968 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
969 sockmap)
970 server.start()
971 addr = ('localhost', server.port)
Vinay Sajip4c3f4782012-04-22 18:19:50 +0100972 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log', timeout=5.0)
Vinay Sajipa463d252011-04-30 21:52:48 +0100973 self.assertEqual(h.toaddrs, ['you'])
974 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100975 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100976 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100977 h.handle(r)
Vinay Sajip17160fd2012-03-15 12:02:08 +0000978 self.handled.wait(5.0) # 14314: don't wait forever
Vinay Sajipa463d252011-04-30 21:52:48 +0100979 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +0000980 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +0100981 self.assertEqual(len(self.messages), 1)
982 peer, mailfrom, rcpttos, data = self.messages[0]
983 self.assertEqual(mailfrom, 'me')
984 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100985 self.assertIn('\nSubject: Log\n', data)
Vinay Sajipa463d252011-04-30 21:52:48 +0100986 self.assertTrue(data.endswith('\n\nHello'))
987 h.close()
988
989 def process_message(self, *args):
990 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100991 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100992
Christian Heimes180510d2008-03-03 19:15:45 +0000993class MemoryHandlerTest(BaseTest):
994
995 """Tests for the MemoryHandler."""
996
997 # Do not bother with a logger name group.
998 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
999
1000 def setUp(self):
1001 BaseTest.setUp(self)
1002 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1003 self.root_hdlr)
1004 self.mem_logger = logging.getLogger('mem')
1005 self.mem_logger.propagate = 0
1006 self.mem_logger.addHandler(self.mem_hdlr)
1007
1008 def tearDown(self):
1009 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001010 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001011
1012 def test_flush(self):
1013 # The memory handler flushes to its target handler based on specific
1014 # criteria (message count and message level).
1015 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001016 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001017 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001018 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001019 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001020 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001021 lines = [
1022 ('DEBUG', '1'),
1023 ('INFO', '2'),
1024 ('WARNING', '3'),
1025 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001026 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001027 for n in (4, 14):
1028 for i in range(9):
1029 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001030 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001031 # This will flush because it's the 10th message since the last
1032 # flush.
1033 self.mem_logger.debug(self.next_message())
1034 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001035 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001036
1037 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001038 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001039
1040
1041class ExceptionFormatter(logging.Formatter):
1042 """A special exception formatter."""
1043 def formatException(self, ei):
1044 return "Got a [%s]" % ei[0].__name__
1045
1046
1047class ConfigFileTest(BaseTest):
1048
1049 """Reading logging config from a .ini-style config file."""
1050
1051 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1052
1053 # config0 is a standard configuration.
1054 config0 = """
1055 [loggers]
1056 keys=root
1057
1058 [handlers]
1059 keys=hand1
1060
1061 [formatters]
1062 keys=form1
1063
1064 [logger_root]
1065 level=WARNING
1066 handlers=hand1
1067
1068 [handler_hand1]
1069 class=StreamHandler
1070 level=NOTSET
1071 formatter=form1
1072 args=(sys.stdout,)
1073
1074 [formatter_form1]
1075 format=%(levelname)s ++ %(message)s
1076 datefmt=
1077 """
1078
1079 # config1 adds a little to the standard configuration.
1080 config1 = """
1081 [loggers]
1082 keys=root,parser
1083
1084 [handlers]
1085 keys=hand1
1086
1087 [formatters]
1088 keys=form1
1089
1090 [logger_root]
1091 level=WARNING
1092 handlers=
1093
1094 [logger_parser]
1095 level=DEBUG
1096 handlers=hand1
1097 propagate=1
1098 qualname=compiler.parser
1099
1100 [handler_hand1]
1101 class=StreamHandler
1102 level=NOTSET
1103 formatter=form1
1104 args=(sys.stdout,)
1105
1106 [formatter_form1]
1107 format=%(levelname)s ++ %(message)s
1108 datefmt=
1109 """
1110
Vinay Sajip3f84b072011-03-07 17:49:33 +00001111 # config1a moves the handler to the root.
1112 config1a = """
1113 [loggers]
1114 keys=root,parser
1115
1116 [handlers]
1117 keys=hand1
1118
1119 [formatters]
1120 keys=form1
1121
1122 [logger_root]
1123 level=WARNING
1124 handlers=hand1
1125
1126 [logger_parser]
1127 level=DEBUG
1128 handlers=
1129 propagate=1
1130 qualname=compiler.parser
1131
1132 [handler_hand1]
1133 class=StreamHandler
1134 level=NOTSET
1135 formatter=form1
1136 args=(sys.stdout,)
1137
1138 [formatter_form1]
1139 format=%(levelname)s ++ %(message)s
1140 datefmt=
1141 """
1142
Christian Heimes180510d2008-03-03 19:15:45 +00001143 # config2 has a subtle configuration error that should be reported
1144 config2 = config1.replace("sys.stdout", "sys.stbout")
1145
1146 # config3 has a less subtle configuration error
1147 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1148
1149 # config4 specifies a custom formatter class to be loaded
1150 config4 = """
1151 [loggers]
1152 keys=root
1153
1154 [handlers]
1155 keys=hand1
1156
1157 [formatters]
1158 keys=form1
1159
1160 [logger_root]
1161 level=NOTSET
1162 handlers=hand1
1163
1164 [handler_hand1]
1165 class=StreamHandler
1166 level=NOTSET
1167 formatter=form1
1168 args=(sys.stdout,)
1169
1170 [formatter_form1]
1171 class=""" + __name__ + """.ExceptionFormatter
1172 format=%(levelname)s:%(name)s:%(message)s
1173 datefmt=
1174 """
1175
Georg Brandl3dbca812008-07-23 16:10:53 +00001176 # config5 specifies a custom handler class to be loaded
1177 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1178
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001179 # config6 uses ', ' delimiters in the handlers and formatters sections
1180 config6 = """
1181 [loggers]
1182 keys=root,parser
1183
1184 [handlers]
1185 keys=hand1, hand2
1186
1187 [formatters]
1188 keys=form1, form2
1189
1190 [logger_root]
1191 level=WARNING
1192 handlers=
1193
1194 [logger_parser]
1195 level=DEBUG
1196 handlers=hand1
1197 propagate=1
1198 qualname=compiler.parser
1199
1200 [handler_hand1]
1201 class=StreamHandler
1202 level=NOTSET
1203 formatter=form1
1204 args=(sys.stdout,)
1205
1206 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001207 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001208 level=NOTSET
1209 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001210 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001211
1212 [formatter_form1]
1213 format=%(levelname)s ++ %(message)s
1214 datefmt=
1215
1216 [formatter_form2]
1217 format=%(message)s
1218 datefmt=
1219 """
1220
Vinay Sajip3f84b072011-03-07 17:49:33 +00001221 # config7 adds a compiler logger.
1222 config7 = """
1223 [loggers]
1224 keys=root,parser,compiler
1225
1226 [handlers]
1227 keys=hand1
1228
1229 [formatters]
1230 keys=form1
1231
1232 [logger_root]
1233 level=WARNING
1234 handlers=hand1
1235
1236 [logger_compiler]
1237 level=DEBUG
1238 handlers=
1239 propagate=1
1240 qualname=compiler
1241
1242 [logger_parser]
1243 level=DEBUG
1244 handlers=
1245 propagate=1
1246 qualname=compiler.parser
1247
1248 [handler_hand1]
1249 class=StreamHandler
1250 level=NOTSET
1251 formatter=form1
1252 args=(sys.stdout,)
1253
1254 [formatter_form1]
1255 format=%(levelname)s ++ %(message)s
1256 datefmt=
1257 """
1258
Christian Heimes180510d2008-03-03 19:15:45 +00001259 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001260 file = io.StringIO(textwrap.dedent(conf))
1261 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001262
1263 def test_config0_ok(self):
1264 # A simple config file which overrides the default settings.
1265 with captured_stdout() as output:
1266 self.apply_config(self.config0)
1267 logger = logging.getLogger()
1268 # Won't output anything
1269 logger.info(self.next_message())
1270 # Outputs a message
1271 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001272 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001273 ('ERROR', '2'),
1274 ], stream=output)
1275 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001276 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001277
Georg Brandl3dbca812008-07-23 16:10:53 +00001278 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001279 # A config file defining a sub-parser as well.
1280 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001281 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001282 logger = logging.getLogger("compiler.parser")
1283 # Both will output a message
1284 logger.info(self.next_message())
1285 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001286 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001287 ('INFO', '1'),
1288 ('ERROR', '2'),
1289 ], stream=output)
1290 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001291 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001292
1293 def test_config2_failure(self):
1294 # A simple config file which overrides the default settings.
1295 self.assertRaises(Exception, self.apply_config, self.config2)
1296
1297 def test_config3_failure(self):
1298 # A simple config file which overrides the default settings.
1299 self.assertRaises(Exception, self.apply_config, self.config3)
1300
1301 def test_config4_ok(self):
1302 # A config file specifying a custom formatter class.
1303 with captured_stdout() as output:
1304 self.apply_config(self.config4)
1305 logger = logging.getLogger()
1306 try:
1307 raise RuntimeError()
1308 except RuntimeError:
1309 logging.exception("just testing")
1310 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001311 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001312 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1313 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001314 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001315
Georg Brandl3dbca812008-07-23 16:10:53 +00001316 def test_config5_ok(self):
1317 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001318
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001319 def test_config6_ok(self):
1320 self.test_config1_ok(config=self.config6)
1321
Vinay Sajip3f84b072011-03-07 17:49:33 +00001322 def test_config7_ok(self):
1323 with captured_stdout() as output:
1324 self.apply_config(self.config1a)
1325 logger = logging.getLogger("compiler.parser")
1326 # See issue #11424. compiler-hyphenated sorts
1327 # between compiler and compiler.xyz and this
1328 # was preventing compiler.xyz from being included
1329 # in the child loggers of compiler because of an
1330 # overzealous loop termination condition.
1331 hyphenated = logging.getLogger('compiler-hyphenated')
1332 # All will output a message
1333 logger.info(self.next_message())
1334 logger.error(self.next_message())
1335 hyphenated.critical(self.next_message())
1336 self.assert_log_lines([
1337 ('INFO', '1'),
1338 ('ERROR', '2'),
1339 ('CRITICAL', '3'),
1340 ], stream=output)
1341 # Original logger output is empty.
1342 self.assert_log_lines([])
1343 with captured_stdout() as output:
1344 self.apply_config(self.config7)
1345 logger = logging.getLogger("compiler.parser")
1346 self.assertFalse(logger.disabled)
1347 # Both will output a message
1348 logger.info(self.next_message())
1349 logger.error(self.next_message())
1350 logger = logging.getLogger("compiler.lexer")
1351 # Both will output a message
1352 logger.info(self.next_message())
1353 logger.error(self.next_message())
1354 # Will not appear
1355 hyphenated.critical(self.next_message())
1356 self.assert_log_lines([
1357 ('INFO', '4'),
1358 ('ERROR', '5'),
1359 ('INFO', '6'),
1360 ('ERROR', '7'),
1361 ], stream=output)
1362 # Original logger output is empty.
1363 self.assert_log_lines([])
1364
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001365
Victor Stinner45df8202010-04-28 22:31:17 +00001366@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001367class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001368
Christian Heimes180510d2008-03-03 19:15:45 +00001369 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001370
Christian Heimes180510d2008-03-03 19:15:45 +00001371 def setUp(self):
1372 """Set up a TCP server to receive log messages, and a SocketHandler
1373 pointing to that server's address and port."""
1374 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001375 addr = ('localhost', 0)
1376 self.server = server = TestTCPServer(addr, self.handle_socket,
1377 0.01)
1378 server.start()
1379 server.ready.wait()
1380 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1381 server.port)
1382 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001383 self.root_logger.removeHandler(self.root_logger.handlers[0])
1384 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001385 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001386
Christian Heimes180510d2008-03-03 19:15:45 +00001387 def tearDown(self):
1388 """Shutdown the TCP server."""
1389 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001390 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001391 self.root_logger.removeHandler(self.sock_hdlr)
1392 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001393 finally:
1394 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001395
Vinay Sajip7367d082011-05-02 13:17:27 +01001396 def handle_socket(self, request):
1397 conn = request.connection
1398 while True:
1399 chunk = conn.recv(4)
1400 if len(chunk) < 4:
1401 break
1402 slen = struct.unpack(">L", chunk)[0]
1403 chunk = conn.recv(slen)
1404 while len(chunk) < slen:
1405 chunk = chunk + conn.recv(slen - len(chunk))
1406 obj = pickle.loads(chunk)
1407 record = logging.makeLogRecord(obj)
1408 self.log_output += record.msg + '\n'
1409 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001410
Christian Heimes180510d2008-03-03 19:15:45 +00001411 def test_output(self):
1412 # The log message sent to the SocketHandler is properly received.
1413 logger = logging.getLogger("tcp")
1414 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001415 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001416 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001417 self.handled.acquire()
1418 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001419
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001420 def test_noserver(self):
1421 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001422 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001423 #The logging call should try to connect, which should fail
1424 try:
1425 raise RuntimeError('Deliberate mistake')
1426 except RuntimeError:
1427 self.root_logger.exception('Never sent')
1428 self.root_logger.error('Never sent, either')
1429 now = time.time()
1430 self.assertTrue(self.sock_hdlr.retryTime > now)
1431 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1432 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001433
Vinay Sajip7367d082011-05-02 13:17:27 +01001434
1435@unittest.skipUnless(threading, 'Threading required for this test.')
1436class DatagramHandlerTest(BaseTest):
1437
1438 """Test for DatagramHandler."""
1439
1440 def setUp(self):
1441 """Set up a UDP server to receive log messages, and a DatagramHandler
1442 pointing to that server's address and port."""
1443 BaseTest.setUp(self)
1444 addr = ('localhost', 0)
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001445 self.server = server = TestUDPServer(addr, self.handle_datagram, 0.01)
Vinay Sajip7367d082011-05-02 13:17:27 +01001446 server.start()
1447 server.ready.wait()
1448 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1449 server.port)
1450 self.log_output = ''
1451 self.root_logger.removeHandler(self.root_logger.handlers[0])
1452 self.root_logger.addHandler(self.sock_hdlr)
1453 self.handled = threading.Event()
1454
1455 def tearDown(self):
1456 """Shutdown the UDP server."""
1457 try:
1458 self.server.stop(2.0)
1459 self.root_logger.removeHandler(self.sock_hdlr)
1460 self.sock_hdlr.close()
1461 finally:
1462 BaseTest.tearDown(self)
1463
1464 def handle_datagram(self, request):
1465 slen = struct.pack('>L', 0) # length of prefix
1466 packet = request.packet[len(slen):]
1467 obj = pickle.loads(packet)
1468 record = logging.makeLogRecord(obj)
1469 self.log_output += record.msg + '\n'
1470 self.handled.set()
1471
1472 def test_output(self):
1473 # The log message sent to the DatagramHandler is properly received.
1474 logger = logging.getLogger("udp")
1475 logger.error("spam")
1476 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001477 self.handled.clear()
1478 logger.error("eggs")
1479 self.handled.wait()
1480 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001481
1482
1483@unittest.skipUnless(threading, 'Threading required for this test.')
1484class SysLogHandlerTest(BaseTest):
1485
1486 """Test for SysLogHandler using UDP."""
1487
1488 def setUp(self):
1489 """Set up a UDP server to receive log messages, and a SysLogHandler
1490 pointing to that server's address and port."""
1491 BaseTest.setUp(self)
1492 addr = ('localhost', 0)
1493 self.server = server = TestUDPServer(addr, self.handle_datagram,
1494 0.01)
1495 server.start()
1496 server.ready.wait()
1497 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1498 server.port))
1499 self.log_output = ''
1500 self.root_logger.removeHandler(self.root_logger.handlers[0])
1501 self.root_logger.addHandler(self.sl_hdlr)
1502 self.handled = threading.Event()
1503
1504 def tearDown(self):
1505 """Shutdown the UDP server."""
1506 try:
1507 self.server.stop(2.0)
1508 self.root_logger.removeHandler(self.sl_hdlr)
1509 self.sl_hdlr.close()
1510 finally:
1511 BaseTest.tearDown(self)
1512
1513 def handle_datagram(self, request):
1514 self.log_output = request.packet
1515 self.handled.set()
1516
1517 def test_output(self):
1518 # The log message sent to the SysLogHandler is properly received.
1519 logger = logging.getLogger("slh")
1520 logger.error("sp\xe4m")
1521 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001522 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001523 self.handled.clear()
1524 self.sl_hdlr.append_nul = False
1525 logger.error("sp\xe4m")
1526 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001527 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001528 self.handled.clear()
1529 self.sl_hdlr.ident = "h\xe4m-"
1530 logger.error("sp\xe4m")
1531 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001532 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001533
1534
1535@unittest.skipUnless(threading, 'Threading required for this test.')
1536class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001537 """Test for HTTPHandler."""
1538
Vinay Sajip9ba87612011-05-21 11:32:15 +01001539 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1540MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1541zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1542aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1543AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
15440tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
15455LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1546piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
15472z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1548i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1549PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1550E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1551rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1552/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1553-----END RSA PRIVATE KEY-----
1554-----BEGIN CERTIFICATE-----
1555MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1556BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1557EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1558xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1559l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1560vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1561BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1562S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1563BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15644f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15651c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1566IRbTpvlHWPjsSvHz0ZOH
1567-----END CERTIFICATE-----"""
1568
Vinay Sajip7367d082011-05-02 13:17:27 +01001569 def setUp(self):
1570 """Set up an HTTP server to receive log messages, and a HTTPHandler
1571 pointing to that server's address and port."""
1572 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001573 self.handled = threading.Event()
1574
Vinay Sajip7367d082011-05-02 13:17:27 +01001575 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001576 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001577 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001578 if self.command == 'POST':
1579 try:
1580 rlen = int(request.headers['Content-Length'])
1581 self.post_data = request.rfile.read(rlen)
1582 except:
1583 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001584 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001585 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001586 self.handled.set()
1587
1588 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001589 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001590 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001591 root_logger = self.root_logger
1592 root_logger.removeHandler(self.root_logger.handlers[0])
1593 for secure in (False, True):
1594 addr = ('localhost', 0)
1595 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001596 try:
1597 import ssl
1598 fd, fn = tempfile.mkstemp()
1599 os.close(fd)
1600 with open(fn, 'w') as f:
1601 f.write(self.PEMFILE)
1602 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1603 sslctx.load_cert_chain(fn)
1604 os.unlink(fn)
1605 except ImportError:
1606 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001607 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001608 sslctx = None
1609 self.server = server = TestHTTPServer(addr, self.handle_request,
1610 0.01, sslctx=sslctx)
1611 server.start()
1612 server.ready.wait()
1613 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001614 secure_client = secure and sslctx
1615 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1616 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001617 self.log_data = None
1618 root_logger.addHandler(self.h_hdlr)
1619
1620 for method in ('GET', 'POST'):
1621 self.h_hdlr.method = method
1622 self.handled.clear()
1623 msg = "sp\xe4m"
1624 logger.error(msg)
1625 self.handled.wait()
1626 self.assertEqual(self.log_data.path, '/frob')
1627 self.assertEqual(self.command, method)
1628 if method == 'GET':
1629 d = parse_qs(self.log_data.query)
1630 else:
1631 d = parse_qs(self.post_data.decode('utf-8'))
1632 self.assertEqual(d['name'], ['http'])
1633 self.assertEqual(d['funcName'], ['test_output'])
1634 self.assertEqual(d['msg'], [msg])
1635
1636 self.server.stop(2.0)
1637 self.root_logger.removeHandler(self.h_hdlr)
1638 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001639
Christian Heimes180510d2008-03-03 19:15:45 +00001640class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001641
Christian Heimes180510d2008-03-03 19:15:45 +00001642 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001643
Christian Heimes180510d2008-03-03 19:15:45 +00001644 def setUp(self):
1645 """Create a dict to remember potentially destroyed objects."""
1646 BaseTest.setUp(self)
1647 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001648
Christian Heimes180510d2008-03-03 19:15:45 +00001649 def _watch_for_survival(self, *args):
1650 """Watch the given objects for survival, by creating weakrefs to
1651 them."""
1652 for obj in args:
1653 key = id(obj), repr(obj)
1654 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001655
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001656 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001657 """Assert that all objects watched for survival have survived."""
1658 # Trigger cycle breaking.
1659 gc.collect()
1660 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001661 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001662 if ref() is None:
1663 dead.append(repr_)
1664 if dead:
1665 self.fail("%d objects should have survived "
1666 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001667
Christian Heimes180510d2008-03-03 19:15:45 +00001668 def test_persistent_loggers(self):
1669 # Logger objects are persistent and retain their configuration, even
1670 # if visible references are destroyed.
1671 self.root_logger.setLevel(logging.INFO)
1672 foo = logging.getLogger("foo")
1673 self._watch_for_survival(foo)
1674 foo.setLevel(logging.DEBUG)
1675 self.root_logger.debug(self.next_message())
1676 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001677 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001678 ('foo', 'DEBUG', '2'),
1679 ])
1680 del foo
1681 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001682 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001683 # foo has retained its settings.
1684 bar = logging.getLogger("foo")
1685 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001686 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001687 ('foo', 'DEBUG', '2'),
1688 ('foo', 'DEBUG', '3'),
1689 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001690
Benjamin Petersonf91df042009-02-13 02:50:59 +00001691
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001692class EncodingTest(BaseTest):
1693 def test_encoding_plain_file(self):
1694 # In Python 2.x, a plain file object is treated as having no encoding.
1695 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001696 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1697 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001698 # the non-ascii data we write to the log.
1699 data = "foo\x80"
1700 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001701 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001702 log.addHandler(handler)
1703 try:
1704 # write non-ascii data to the log.
1705 log.warning(data)
1706 finally:
1707 log.removeHandler(handler)
1708 handler.close()
1709 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001710 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001711 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001712 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001713 finally:
1714 f.close()
1715 finally:
1716 if os.path.isfile(fn):
1717 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001718
Benjamin Petersonf91df042009-02-13 02:50:59 +00001719 def test_encoding_cyrillic_unicode(self):
1720 log = logging.getLogger("test")
1721 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1722 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1723 #Ensure it's written in a Cyrillic encoding
1724 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001725 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001726 stream = io.BytesIO()
1727 writer = writer_class(stream, 'strict')
1728 handler = logging.StreamHandler(writer)
1729 log.addHandler(handler)
1730 try:
1731 log.warning(message)
1732 finally:
1733 log.removeHandler(handler)
1734 handler.close()
1735 # check we wrote exactly those bytes, ignoring trailing \n etc
1736 s = stream.getvalue()
1737 #Compare against what the data should be when encoded in CP-1251
1738 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1739
1740
Georg Brandlf9734072008-12-07 15:30:06 +00001741class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001742
Georg Brandlf9734072008-12-07 15:30:06 +00001743 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001744 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001745 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001746 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001747 warnings.filterwarnings("always", category=UserWarning)
1748 stream = io.StringIO()
1749 h = logging.StreamHandler(stream)
1750 logger = logging.getLogger("py.warnings")
1751 logger.addHandler(h)
1752 warnings.warn("I'm warning you...")
1753 logger.removeHandler(h)
1754 s = stream.getvalue()
1755 h.close()
1756 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001757
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001758 #See if an explicit file uses the original implementation
1759 a_file = io.StringIO()
1760 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1761 a_file, "Dummy line")
1762 s = a_file.getvalue()
1763 a_file.close()
1764 self.assertEqual(s,
1765 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1766
1767 def test_warnings_no_handlers(self):
1768 with warnings.catch_warnings():
1769 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001770 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001771
1772 # confirm our assumption: no loggers are set
1773 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001774 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001775
1776 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001777 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001778 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001779
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001780
1781def formatFunc(format, datefmt=None):
1782 return logging.Formatter(format, datefmt)
1783
1784def handlerFunc():
1785 return logging.StreamHandler()
1786
1787class CustomHandler(logging.StreamHandler):
1788 pass
1789
1790class ConfigDictTest(BaseTest):
1791
1792 """Reading logging config from a dictionary."""
1793
1794 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1795
1796 # config0 is a standard configuration.
1797 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001798 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001799 'formatters': {
1800 'form1' : {
1801 'format' : '%(levelname)s ++ %(message)s',
1802 },
1803 },
1804 'handlers' : {
1805 'hand1' : {
1806 'class' : 'logging.StreamHandler',
1807 'formatter' : 'form1',
1808 'level' : 'NOTSET',
1809 'stream' : 'ext://sys.stdout',
1810 },
1811 },
1812 'root' : {
1813 'level' : 'WARNING',
1814 'handlers' : ['hand1'],
1815 },
1816 }
1817
1818 # config1 adds a little to the standard configuration.
1819 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001820 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001821 'formatters': {
1822 'form1' : {
1823 'format' : '%(levelname)s ++ %(message)s',
1824 },
1825 },
1826 'handlers' : {
1827 'hand1' : {
1828 'class' : 'logging.StreamHandler',
1829 'formatter' : 'form1',
1830 'level' : 'NOTSET',
1831 'stream' : 'ext://sys.stdout',
1832 },
1833 },
1834 'loggers' : {
1835 'compiler.parser' : {
1836 'level' : 'DEBUG',
1837 'handlers' : ['hand1'],
1838 },
1839 },
1840 'root' : {
1841 'level' : 'WARNING',
1842 },
1843 }
1844
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001845 # config1a moves the handler to the root. Used with config8a
1846 config1a = {
1847 'version': 1,
1848 'formatters': {
1849 'form1' : {
1850 'format' : '%(levelname)s ++ %(message)s',
1851 },
1852 },
1853 'handlers' : {
1854 'hand1' : {
1855 'class' : 'logging.StreamHandler',
1856 'formatter' : 'form1',
1857 'level' : 'NOTSET',
1858 'stream' : 'ext://sys.stdout',
1859 },
1860 },
1861 'loggers' : {
1862 'compiler.parser' : {
1863 'level' : 'DEBUG',
1864 },
1865 },
1866 'root' : {
1867 'level' : 'WARNING',
1868 'handlers' : ['hand1'],
1869 },
1870 }
1871
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001872 # config2 has a subtle configuration error that should be reported
1873 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001874 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001875 'formatters': {
1876 'form1' : {
1877 'format' : '%(levelname)s ++ %(message)s',
1878 },
1879 },
1880 'handlers' : {
1881 'hand1' : {
1882 'class' : 'logging.StreamHandler',
1883 'formatter' : 'form1',
1884 'level' : 'NOTSET',
1885 'stream' : 'ext://sys.stdbout',
1886 },
1887 },
1888 'loggers' : {
1889 'compiler.parser' : {
1890 'level' : 'DEBUG',
1891 'handlers' : ['hand1'],
1892 },
1893 },
1894 'root' : {
1895 'level' : 'WARNING',
1896 },
1897 }
1898
1899 #As config1 but with a misspelt level on a handler
1900 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001901 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001902 'formatters': {
1903 'form1' : {
1904 'format' : '%(levelname)s ++ %(message)s',
1905 },
1906 },
1907 'handlers' : {
1908 'hand1' : {
1909 'class' : 'logging.StreamHandler',
1910 'formatter' : 'form1',
1911 'level' : 'NTOSET',
1912 'stream' : 'ext://sys.stdout',
1913 },
1914 },
1915 'loggers' : {
1916 'compiler.parser' : {
1917 'level' : 'DEBUG',
1918 'handlers' : ['hand1'],
1919 },
1920 },
1921 'root' : {
1922 'level' : 'WARNING',
1923 },
1924 }
1925
1926
1927 #As config1 but with a misspelt level on a logger
1928 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001929 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001930 'formatters': {
1931 'form1' : {
1932 'format' : '%(levelname)s ++ %(message)s',
1933 },
1934 },
1935 'handlers' : {
1936 'hand1' : {
1937 'class' : 'logging.StreamHandler',
1938 'formatter' : 'form1',
1939 'level' : 'NOTSET',
1940 'stream' : 'ext://sys.stdout',
1941 },
1942 },
1943 'loggers' : {
1944 'compiler.parser' : {
1945 'level' : 'DEBUG',
1946 'handlers' : ['hand1'],
1947 },
1948 },
1949 'root' : {
1950 'level' : 'WRANING',
1951 },
1952 }
1953
1954 # config3 has a less subtle configuration error
1955 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001956 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001957 'formatters': {
1958 'form1' : {
1959 'format' : '%(levelname)s ++ %(message)s',
1960 },
1961 },
1962 'handlers' : {
1963 'hand1' : {
1964 'class' : 'logging.StreamHandler',
1965 'formatter' : 'misspelled_name',
1966 'level' : 'NOTSET',
1967 'stream' : 'ext://sys.stdout',
1968 },
1969 },
1970 'loggers' : {
1971 'compiler.parser' : {
1972 'level' : 'DEBUG',
1973 'handlers' : ['hand1'],
1974 },
1975 },
1976 'root' : {
1977 'level' : 'WARNING',
1978 },
1979 }
1980
1981 # config4 specifies a custom formatter class to be loaded
1982 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001983 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001984 'formatters': {
1985 'form1' : {
1986 '()' : __name__ + '.ExceptionFormatter',
1987 'format' : '%(levelname)s:%(name)s:%(message)s',
1988 },
1989 },
1990 'handlers' : {
1991 'hand1' : {
1992 'class' : 'logging.StreamHandler',
1993 'formatter' : 'form1',
1994 'level' : 'NOTSET',
1995 'stream' : 'ext://sys.stdout',
1996 },
1997 },
1998 'root' : {
1999 'level' : 'NOTSET',
2000 'handlers' : ['hand1'],
2001 },
2002 }
2003
2004 # As config4 but using an actual callable rather than a string
2005 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002006 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002007 'formatters': {
2008 'form1' : {
2009 '()' : ExceptionFormatter,
2010 'format' : '%(levelname)s:%(name)s:%(message)s',
2011 },
2012 'form2' : {
2013 '()' : __name__ + '.formatFunc',
2014 'format' : '%(levelname)s:%(name)s:%(message)s',
2015 },
2016 'form3' : {
2017 '()' : formatFunc,
2018 'format' : '%(levelname)s:%(name)s:%(message)s',
2019 },
2020 },
2021 'handlers' : {
2022 'hand1' : {
2023 'class' : 'logging.StreamHandler',
2024 'formatter' : 'form1',
2025 'level' : 'NOTSET',
2026 'stream' : 'ext://sys.stdout',
2027 },
2028 'hand2' : {
2029 '()' : handlerFunc,
2030 },
2031 },
2032 'root' : {
2033 'level' : 'NOTSET',
2034 'handlers' : ['hand1'],
2035 },
2036 }
2037
2038 # config5 specifies a custom handler class to be loaded
2039 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002040 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002041 'formatters': {
2042 'form1' : {
2043 'format' : '%(levelname)s ++ %(message)s',
2044 },
2045 },
2046 'handlers' : {
2047 'hand1' : {
2048 'class' : __name__ + '.CustomHandler',
2049 'formatter' : 'form1',
2050 'level' : 'NOTSET',
2051 'stream' : 'ext://sys.stdout',
2052 },
2053 },
2054 'loggers' : {
2055 'compiler.parser' : {
2056 'level' : 'DEBUG',
2057 'handlers' : ['hand1'],
2058 },
2059 },
2060 'root' : {
2061 'level' : 'WARNING',
2062 },
2063 }
2064
2065 # config6 specifies a custom handler class to be loaded
2066 # but has bad arguments
2067 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002068 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002069 'formatters': {
2070 'form1' : {
2071 'format' : '%(levelname)s ++ %(message)s',
2072 },
2073 },
2074 'handlers' : {
2075 'hand1' : {
2076 'class' : __name__ + '.CustomHandler',
2077 'formatter' : 'form1',
2078 'level' : 'NOTSET',
2079 'stream' : 'ext://sys.stdout',
2080 '9' : 'invalid parameter name',
2081 },
2082 },
2083 'loggers' : {
2084 'compiler.parser' : {
2085 'level' : 'DEBUG',
2086 'handlers' : ['hand1'],
2087 },
2088 },
2089 'root' : {
2090 'level' : 'WARNING',
2091 },
2092 }
2093
2094 #config 7 does not define compiler.parser but defines compiler.lexer
2095 #so compiler.parser should be disabled after applying it
2096 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002097 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002098 'formatters': {
2099 'form1' : {
2100 'format' : '%(levelname)s ++ %(message)s',
2101 },
2102 },
2103 'handlers' : {
2104 'hand1' : {
2105 'class' : 'logging.StreamHandler',
2106 'formatter' : 'form1',
2107 'level' : 'NOTSET',
2108 'stream' : 'ext://sys.stdout',
2109 },
2110 },
2111 'loggers' : {
2112 'compiler.lexer' : {
2113 'level' : 'DEBUG',
2114 'handlers' : ['hand1'],
2115 },
2116 },
2117 'root' : {
2118 'level' : 'WARNING',
2119 },
2120 }
2121
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002122 # config8 defines both compiler and compiler.lexer
2123 # so compiler.parser should not be disabled (since
2124 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002125 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002126 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002127 'disable_existing_loggers' : False,
2128 'formatters': {
2129 'form1' : {
2130 'format' : '%(levelname)s ++ %(message)s',
2131 },
2132 },
2133 'handlers' : {
2134 'hand1' : {
2135 'class' : 'logging.StreamHandler',
2136 'formatter' : 'form1',
2137 'level' : 'NOTSET',
2138 'stream' : 'ext://sys.stdout',
2139 },
2140 },
2141 'loggers' : {
2142 'compiler' : {
2143 'level' : 'DEBUG',
2144 'handlers' : ['hand1'],
2145 },
2146 'compiler.lexer' : {
2147 },
2148 },
2149 'root' : {
2150 'level' : 'WARNING',
2151 },
2152 }
2153
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002154 # config8a disables existing loggers
2155 config8a = {
2156 'version': 1,
2157 'disable_existing_loggers' : True,
2158 'formatters': {
2159 'form1' : {
2160 'format' : '%(levelname)s ++ %(message)s',
2161 },
2162 },
2163 'handlers' : {
2164 'hand1' : {
2165 'class' : 'logging.StreamHandler',
2166 'formatter' : 'form1',
2167 'level' : 'NOTSET',
2168 'stream' : 'ext://sys.stdout',
2169 },
2170 },
2171 'loggers' : {
2172 'compiler' : {
2173 'level' : 'DEBUG',
2174 'handlers' : ['hand1'],
2175 },
2176 'compiler.lexer' : {
2177 },
2178 },
2179 'root' : {
2180 'level' : 'WARNING',
2181 },
2182 }
2183
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002184 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002185 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002186 'formatters': {
2187 'form1' : {
2188 'format' : '%(levelname)s ++ %(message)s',
2189 },
2190 },
2191 'handlers' : {
2192 'hand1' : {
2193 'class' : 'logging.StreamHandler',
2194 'formatter' : 'form1',
2195 'level' : 'WARNING',
2196 'stream' : 'ext://sys.stdout',
2197 },
2198 },
2199 'loggers' : {
2200 'compiler.parser' : {
2201 'level' : 'WARNING',
2202 'handlers' : ['hand1'],
2203 },
2204 },
2205 'root' : {
2206 'level' : 'NOTSET',
2207 },
2208 }
2209
2210 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002211 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002212 'incremental' : True,
2213 'handlers' : {
2214 'hand1' : {
2215 'level' : 'WARNING',
2216 },
2217 },
2218 'loggers' : {
2219 'compiler.parser' : {
2220 'level' : 'INFO',
2221 },
2222 },
2223 }
2224
2225 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002226 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002227 'incremental' : True,
2228 'handlers' : {
2229 'hand1' : {
2230 'level' : 'INFO',
2231 },
2232 },
2233 'loggers' : {
2234 'compiler.parser' : {
2235 'level' : 'INFO',
2236 },
2237 },
2238 }
2239
2240 #As config1 but with a filter added
2241 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002242 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002243 'formatters': {
2244 'form1' : {
2245 'format' : '%(levelname)s ++ %(message)s',
2246 },
2247 },
2248 'filters' : {
2249 'filt1' : {
2250 'name' : 'compiler.parser',
2251 },
2252 },
2253 'handlers' : {
2254 'hand1' : {
2255 'class' : 'logging.StreamHandler',
2256 'formatter' : 'form1',
2257 'level' : 'NOTSET',
2258 'stream' : 'ext://sys.stdout',
2259 'filters' : ['filt1'],
2260 },
2261 },
2262 'loggers' : {
2263 'compiler.parser' : {
2264 'level' : 'DEBUG',
2265 'filters' : ['filt1'],
2266 },
2267 },
2268 'root' : {
2269 'level' : 'WARNING',
2270 'handlers' : ['hand1'],
2271 },
2272 }
2273
2274 #As config1 but using cfg:// references
2275 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002276 'version': 1,
2277 'true_formatters': {
2278 'form1' : {
2279 'format' : '%(levelname)s ++ %(message)s',
2280 },
2281 },
2282 'handler_configs': {
2283 'hand1' : {
2284 'class' : 'logging.StreamHandler',
2285 'formatter' : 'form1',
2286 'level' : 'NOTSET',
2287 'stream' : 'ext://sys.stdout',
2288 },
2289 },
2290 'formatters' : 'cfg://true_formatters',
2291 'handlers' : {
2292 'hand1' : 'cfg://handler_configs[hand1]',
2293 },
2294 'loggers' : {
2295 'compiler.parser' : {
2296 'level' : 'DEBUG',
2297 'handlers' : ['hand1'],
2298 },
2299 },
2300 'root' : {
2301 'level' : 'WARNING',
2302 },
2303 }
2304
2305 #As config11 but missing the version key
2306 config12 = {
2307 'true_formatters': {
2308 'form1' : {
2309 'format' : '%(levelname)s ++ %(message)s',
2310 },
2311 },
2312 'handler_configs': {
2313 'hand1' : {
2314 'class' : 'logging.StreamHandler',
2315 'formatter' : 'form1',
2316 'level' : 'NOTSET',
2317 'stream' : 'ext://sys.stdout',
2318 },
2319 },
2320 'formatters' : 'cfg://true_formatters',
2321 'handlers' : {
2322 'hand1' : 'cfg://handler_configs[hand1]',
2323 },
2324 'loggers' : {
2325 'compiler.parser' : {
2326 'level' : 'DEBUG',
2327 'handlers' : ['hand1'],
2328 },
2329 },
2330 'root' : {
2331 'level' : 'WARNING',
2332 },
2333 }
2334
2335 #As config11 but using an unsupported version
2336 config13 = {
2337 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002338 'true_formatters': {
2339 'form1' : {
2340 'format' : '%(levelname)s ++ %(message)s',
2341 },
2342 },
2343 'handler_configs': {
2344 'hand1' : {
2345 'class' : 'logging.StreamHandler',
2346 'formatter' : 'form1',
2347 'level' : 'NOTSET',
2348 'stream' : 'ext://sys.stdout',
2349 },
2350 },
2351 'formatters' : 'cfg://true_formatters',
2352 'handlers' : {
2353 'hand1' : 'cfg://handler_configs[hand1]',
2354 },
2355 'loggers' : {
2356 'compiler.parser' : {
2357 'level' : 'DEBUG',
2358 'handlers' : ['hand1'],
2359 },
2360 },
2361 'root' : {
2362 'level' : 'WARNING',
2363 },
2364 }
2365
2366 def apply_config(self, conf):
2367 logging.config.dictConfig(conf)
2368
2369 def test_config0_ok(self):
2370 # A simple config which overrides the default settings.
2371 with captured_stdout() as output:
2372 self.apply_config(self.config0)
2373 logger = logging.getLogger()
2374 # Won't output anything
2375 logger.info(self.next_message())
2376 # Outputs a message
2377 logger.error(self.next_message())
2378 self.assert_log_lines([
2379 ('ERROR', '2'),
2380 ], stream=output)
2381 # Original logger output is empty.
2382 self.assert_log_lines([])
2383
2384 def test_config1_ok(self, config=config1):
2385 # A config defining a sub-parser as well.
2386 with captured_stdout() as output:
2387 self.apply_config(config)
2388 logger = logging.getLogger("compiler.parser")
2389 # Both will output a message
2390 logger.info(self.next_message())
2391 logger.error(self.next_message())
2392 self.assert_log_lines([
2393 ('INFO', '1'),
2394 ('ERROR', '2'),
2395 ], stream=output)
2396 # Original logger output is empty.
2397 self.assert_log_lines([])
2398
2399 def test_config2_failure(self):
2400 # A simple config which overrides the default settings.
2401 self.assertRaises(Exception, self.apply_config, self.config2)
2402
2403 def test_config2a_failure(self):
2404 # A simple config which overrides the default settings.
2405 self.assertRaises(Exception, self.apply_config, self.config2a)
2406
2407 def test_config2b_failure(self):
2408 # A simple config which overrides the default settings.
2409 self.assertRaises(Exception, self.apply_config, self.config2b)
2410
2411 def test_config3_failure(self):
2412 # A simple config which overrides the default settings.
2413 self.assertRaises(Exception, self.apply_config, self.config3)
2414
2415 def test_config4_ok(self):
2416 # A config specifying a custom formatter class.
2417 with captured_stdout() as output:
2418 self.apply_config(self.config4)
2419 #logger = logging.getLogger()
2420 try:
2421 raise RuntimeError()
2422 except RuntimeError:
2423 logging.exception("just testing")
2424 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002425 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002426 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2427 # Original logger output is empty
2428 self.assert_log_lines([])
2429
2430 def test_config4a_ok(self):
2431 # A config specifying a custom formatter class.
2432 with captured_stdout() as output:
2433 self.apply_config(self.config4a)
2434 #logger = logging.getLogger()
2435 try:
2436 raise RuntimeError()
2437 except RuntimeError:
2438 logging.exception("just testing")
2439 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002440 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002441 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2442 # Original logger output is empty
2443 self.assert_log_lines([])
2444
2445 def test_config5_ok(self):
2446 self.test_config1_ok(config=self.config5)
2447
2448 def test_config6_failure(self):
2449 self.assertRaises(Exception, self.apply_config, self.config6)
2450
2451 def test_config7_ok(self):
2452 with captured_stdout() as output:
2453 self.apply_config(self.config1)
2454 logger = logging.getLogger("compiler.parser")
2455 # Both will output a message
2456 logger.info(self.next_message())
2457 logger.error(self.next_message())
2458 self.assert_log_lines([
2459 ('INFO', '1'),
2460 ('ERROR', '2'),
2461 ], stream=output)
2462 # Original logger output is empty.
2463 self.assert_log_lines([])
2464 with captured_stdout() as output:
2465 self.apply_config(self.config7)
2466 logger = logging.getLogger("compiler.parser")
2467 self.assertTrue(logger.disabled)
2468 logger = logging.getLogger("compiler.lexer")
2469 # Both will output a message
2470 logger.info(self.next_message())
2471 logger.error(self.next_message())
2472 self.assert_log_lines([
2473 ('INFO', '3'),
2474 ('ERROR', '4'),
2475 ], stream=output)
2476 # Original logger output is empty.
2477 self.assert_log_lines([])
2478
2479 #Same as test_config_7_ok but don't disable old loggers.
2480 def test_config_8_ok(self):
2481 with captured_stdout() as output:
2482 self.apply_config(self.config1)
2483 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002484 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002485 logger.info(self.next_message())
2486 logger.error(self.next_message())
2487 self.assert_log_lines([
2488 ('INFO', '1'),
2489 ('ERROR', '2'),
2490 ], stream=output)
2491 # Original logger output is empty.
2492 self.assert_log_lines([])
2493 with captured_stdout() as output:
2494 self.apply_config(self.config8)
2495 logger = logging.getLogger("compiler.parser")
2496 self.assertFalse(logger.disabled)
2497 # Both will output a message
2498 logger.info(self.next_message())
2499 logger.error(self.next_message())
2500 logger = logging.getLogger("compiler.lexer")
2501 # Both will output a message
2502 logger.info(self.next_message())
2503 logger.error(self.next_message())
2504 self.assert_log_lines([
2505 ('INFO', '3'),
2506 ('ERROR', '4'),
2507 ('INFO', '5'),
2508 ('ERROR', '6'),
2509 ], stream=output)
2510 # Original logger output is empty.
2511 self.assert_log_lines([])
2512
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002513 def test_config_8a_ok(self):
2514 with captured_stdout() as output:
2515 self.apply_config(self.config1a)
2516 logger = logging.getLogger("compiler.parser")
2517 # See issue #11424. compiler-hyphenated sorts
2518 # between compiler and compiler.xyz and this
2519 # was preventing compiler.xyz from being included
2520 # in the child loggers of compiler because of an
2521 # overzealous loop termination condition.
2522 hyphenated = logging.getLogger('compiler-hyphenated')
2523 # All will output a message
2524 logger.info(self.next_message())
2525 logger.error(self.next_message())
2526 hyphenated.critical(self.next_message())
2527 self.assert_log_lines([
2528 ('INFO', '1'),
2529 ('ERROR', '2'),
2530 ('CRITICAL', '3'),
2531 ], stream=output)
2532 # Original logger output is empty.
2533 self.assert_log_lines([])
2534 with captured_stdout() as output:
2535 self.apply_config(self.config8a)
2536 logger = logging.getLogger("compiler.parser")
2537 self.assertFalse(logger.disabled)
2538 # Both will output a message
2539 logger.info(self.next_message())
2540 logger.error(self.next_message())
2541 logger = logging.getLogger("compiler.lexer")
2542 # Both will output a message
2543 logger.info(self.next_message())
2544 logger.error(self.next_message())
2545 # Will not appear
2546 hyphenated.critical(self.next_message())
2547 self.assert_log_lines([
2548 ('INFO', '4'),
2549 ('ERROR', '5'),
2550 ('INFO', '6'),
2551 ('ERROR', '7'),
2552 ], stream=output)
2553 # Original logger output is empty.
2554 self.assert_log_lines([])
2555
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002556 def test_config_9_ok(self):
2557 with captured_stdout() as output:
2558 self.apply_config(self.config9)
2559 logger = logging.getLogger("compiler.parser")
2560 #Nothing will be output since both handler and logger are set to WARNING
2561 logger.info(self.next_message())
2562 self.assert_log_lines([], stream=output)
2563 self.apply_config(self.config9a)
2564 #Nothing will be output since both handler is still set to WARNING
2565 logger.info(self.next_message())
2566 self.assert_log_lines([], stream=output)
2567 self.apply_config(self.config9b)
2568 #Message should now be output
2569 logger.info(self.next_message())
2570 self.assert_log_lines([
2571 ('INFO', '3'),
2572 ], stream=output)
2573
2574 def test_config_10_ok(self):
2575 with captured_stdout() as output:
2576 self.apply_config(self.config10)
2577 logger = logging.getLogger("compiler.parser")
2578 logger.warning(self.next_message())
2579 logger = logging.getLogger('compiler')
2580 #Not output, because filtered
2581 logger.warning(self.next_message())
2582 logger = logging.getLogger('compiler.lexer')
2583 #Not output, because filtered
2584 logger.warning(self.next_message())
2585 logger = logging.getLogger("compiler.parser.codegen")
2586 #Output, as not filtered
2587 logger.error(self.next_message())
2588 self.assert_log_lines([
2589 ('WARNING', '1'),
2590 ('ERROR', '4'),
2591 ], stream=output)
2592
2593 def test_config11_ok(self):
2594 self.test_config1_ok(self.config11)
2595
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002596 def test_config12_failure(self):
2597 self.assertRaises(Exception, self.apply_config, self.config12)
2598
2599 def test_config13_failure(self):
2600 self.assertRaises(Exception, self.apply_config, self.config13)
2601
Victor Stinner45df8202010-04-28 22:31:17 +00002602 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002603 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002604 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002605 # Ask for a randomly assigned port (by using port 0)
2606 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002607 t.start()
2608 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002609 # Now get the port allocated
2610 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002611 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002612 try:
2613 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2614 sock.settimeout(2.0)
2615 sock.connect(('localhost', port))
2616
2617 slen = struct.pack('>L', len(text))
2618 s = slen + text
2619 sentsofar = 0
2620 left = len(s)
2621 while left > 0:
2622 sent = sock.send(s[sentsofar:])
2623 sentsofar += sent
2624 left -= sent
2625 sock.close()
2626 finally:
2627 t.ready.wait(2.0)
2628 logging.config.stopListening()
2629 t.join(2.0)
2630
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002631 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002632 def test_listen_config_10_ok(self):
2633 with captured_stdout() as output:
2634 self.setup_via_listener(json.dumps(self.config10))
2635 logger = logging.getLogger("compiler.parser")
2636 logger.warning(self.next_message())
2637 logger = logging.getLogger('compiler')
2638 #Not output, because filtered
2639 logger.warning(self.next_message())
2640 logger = logging.getLogger('compiler.lexer')
2641 #Not output, because filtered
2642 logger.warning(self.next_message())
2643 logger = logging.getLogger("compiler.parser.codegen")
2644 #Output, as not filtered
2645 logger.error(self.next_message())
2646 self.assert_log_lines([
2647 ('WARNING', '1'),
2648 ('ERROR', '4'),
2649 ], stream=output)
2650
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002651 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002652 def test_listen_config_1_ok(self):
2653 with captured_stdout() as output:
2654 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2655 logger = logging.getLogger("compiler.parser")
2656 # Both will output a message
2657 logger.info(self.next_message())
2658 logger.error(self.next_message())
2659 self.assert_log_lines([
2660 ('INFO', '1'),
2661 ('ERROR', '2'),
2662 ], stream=output)
2663 # Original logger output is empty.
2664 self.assert_log_lines([])
2665
Vinay Sajip373baef2011-04-26 20:05:24 +01002666 def test_baseconfig(self):
2667 d = {
2668 'atuple': (1, 2, 3),
2669 'alist': ['a', 'b', 'c'],
2670 'adict': {'d': 'e', 'f': 3 },
2671 'nest1': ('g', ('h', 'i'), 'j'),
2672 'nest2': ['k', ['l', 'm'], 'n'],
2673 'nest3': ['o', 'cfg://alist', 'p'],
2674 }
2675 bc = logging.config.BaseConfigurator(d)
2676 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2677 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2678 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2679 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2680 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2681 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2682 v = bc.convert('cfg://nest3')
2683 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2684 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2685 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2686 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002687
2688class ManagerTest(BaseTest):
2689 def test_manager_loggerclass(self):
2690 logged = []
2691
2692 class MyLogger(logging.Logger):
2693 def _log(self, level, msg, args, exc_info=None, extra=None):
2694 logged.append(msg)
2695
2696 man = logging.Manager(None)
2697 self.assertRaises(TypeError, man.setLoggerClass, int)
2698 man.setLoggerClass(MyLogger)
2699 logger = man.getLogger('test')
2700 logger.warning('should appear in logged')
2701 logging.warning('should not appear in logged')
2702
2703 self.assertEqual(logged, ['should appear in logged'])
2704
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002705 def test_set_log_record_factory(self):
2706 man = logging.Manager(None)
2707 expected = object()
2708 man.setLogRecordFactory(expected)
2709 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002710
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002711class ChildLoggerTest(BaseTest):
2712 def test_child_loggers(self):
2713 r = logging.getLogger()
2714 l1 = logging.getLogger('abc')
2715 l2 = logging.getLogger('def.ghi')
2716 c1 = r.getChild('xyz')
2717 c2 = r.getChild('uvw.xyz')
2718 self.assertTrue(c1 is logging.getLogger('xyz'))
2719 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2720 c1 = l1.getChild('def')
2721 c2 = c1.getChild('ghi')
2722 c3 = l1.getChild('def.ghi')
2723 self.assertTrue(c1 is logging.getLogger('abc.def'))
2724 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2725 self.assertTrue(c2 is c3)
2726
2727
Vinay Sajip6fac8172010-10-19 20:44:14 +00002728class DerivedLogRecord(logging.LogRecord):
2729 pass
2730
Vinay Sajip61561522010-12-03 11:50:38 +00002731class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002732
2733 def setUp(self):
2734 class CheckingFilter(logging.Filter):
2735 def __init__(self, cls):
2736 self.cls = cls
2737
2738 def filter(self, record):
2739 t = type(record)
2740 if t is not self.cls:
2741 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2742 self.cls)
2743 raise TypeError(msg)
2744 return True
2745
2746 BaseTest.setUp(self)
2747 self.filter = CheckingFilter(DerivedLogRecord)
2748 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002749 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002750
2751 def tearDown(self):
2752 self.root_logger.removeFilter(self.filter)
2753 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002754 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002755
2756 def test_logrecord_class(self):
2757 self.assertRaises(TypeError, self.root_logger.warning,
2758 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002759 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002760 self.root_logger.error(self.next_message())
2761 self.assert_log_lines([
2762 ('root', 'ERROR', '2'),
2763 ])
2764
2765
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002766class QueueHandlerTest(BaseTest):
2767 # Do not bother with a logger name group.
2768 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2769
2770 def setUp(self):
2771 BaseTest.setUp(self)
2772 self.queue = queue.Queue(-1)
2773 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2774 self.que_logger = logging.getLogger('que')
2775 self.que_logger.propagate = False
2776 self.que_logger.setLevel(logging.WARNING)
2777 self.que_logger.addHandler(self.que_hdlr)
2778
2779 def tearDown(self):
2780 self.que_hdlr.close()
2781 BaseTest.tearDown(self)
2782
2783 def test_queue_handler(self):
2784 self.que_logger.debug(self.next_message())
2785 self.assertRaises(queue.Empty, self.queue.get_nowait)
2786 self.que_logger.info(self.next_message())
2787 self.assertRaises(queue.Empty, self.queue.get_nowait)
2788 msg = self.next_message()
2789 self.que_logger.warning(msg)
2790 data = self.queue.get_nowait()
2791 self.assertTrue(isinstance(data, logging.LogRecord))
2792 self.assertEqual(data.name, self.que_logger.name)
2793 self.assertEqual((data.msg, data.args), (msg, None))
2794
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002795 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2796 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002797 def test_queue_listener(self):
2798 handler = TestHandler(Matcher())
2799 listener = logging.handlers.QueueListener(self.queue, handler)
2800 listener.start()
2801 try:
2802 self.que_logger.warning(self.next_message())
2803 self.que_logger.error(self.next_message())
2804 self.que_logger.critical(self.next_message())
2805 finally:
2806 listener.stop()
2807 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2808 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2809 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2810
Vinay Sajip37eb3382011-04-26 20:26:41 +01002811ZERO = datetime.timedelta(0)
2812
2813class UTC(datetime.tzinfo):
2814 def utcoffset(self, dt):
2815 return ZERO
2816
2817 dst = utcoffset
2818
2819 def tzname(self, dt):
2820 return 'UTC'
2821
2822utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002823
Vinay Sajipa39c5712010-10-25 13:57:39 +00002824class FormatterTest(unittest.TestCase):
2825 def setUp(self):
2826 self.common = {
2827 'name': 'formatter.test',
2828 'level': logging.DEBUG,
2829 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2830 'lineno': 42,
2831 'exc_info': None,
2832 'func': None,
2833 'msg': 'Message with %d %s',
2834 'args': (2, 'placeholders'),
2835 }
2836 self.variants = {
2837 }
2838
2839 def get_record(self, name=None):
2840 result = dict(self.common)
2841 if name is not None:
2842 result.update(self.variants[name])
2843 return logging.makeLogRecord(result)
2844
2845 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002846 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002847 r = self.get_record()
2848 f = logging.Formatter('${%(message)s}')
2849 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2850 f = logging.Formatter('%(random)s')
2851 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002852 self.assertFalse(f.usesTime())
2853 f = logging.Formatter('%(asctime)s')
2854 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002855 f = logging.Formatter('%(asctime)-15s')
2856 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002857 f = logging.Formatter('asctime')
2858 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002859
2860 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002861 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002862 r = self.get_record()
2863 f = logging.Formatter('$%{message}%$', style='{')
2864 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2865 f = logging.Formatter('{random}', style='{')
2866 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002867 self.assertFalse(f.usesTime())
2868 f = logging.Formatter('{asctime}', style='{')
2869 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002870 f = logging.Formatter('{asctime!s:15}', style='{')
2871 self.assertTrue(f.usesTime())
2872 f = logging.Formatter('{asctime:15}', style='{')
2873 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002874 f = logging.Formatter('asctime', style='{')
2875 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002876
2877 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002878 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002879 r = self.get_record()
2880 f = logging.Formatter('$message', style='$')
2881 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2882 f = logging.Formatter('$$%${message}%$$', style='$')
2883 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2884 f = logging.Formatter('${random}', style='$')
2885 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002886 self.assertFalse(f.usesTime())
2887 f = logging.Formatter('${asctime}', style='$')
2888 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002889 f = logging.Formatter('${asctime', style='$')
2890 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002891 f = logging.Formatter('$asctime', style='$')
2892 self.assertTrue(f.usesTime())
2893 f = logging.Formatter('asctime', style='$')
2894 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002895
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002896 def test_invalid_style(self):
2897 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2898
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002899 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002900 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002901 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002902 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002903 r.msecs = 123
2904 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002905 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002906 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2907 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002908 f.format(r)
2909 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2910
2911class TestBufferingFormatter(logging.BufferingFormatter):
2912 def formatHeader(self, records):
2913 return '[(%d)' % len(records)
2914
2915 def formatFooter(self, records):
2916 return '(%d)]' % len(records)
2917
2918class BufferingFormatterTest(unittest.TestCase):
2919 def setUp(self):
2920 self.records = [
2921 logging.makeLogRecord({'msg': 'one'}),
2922 logging.makeLogRecord({'msg': 'two'}),
2923 ]
2924
2925 def test_default(self):
2926 f = logging.BufferingFormatter()
2927 self.assertEqual('', f.format([]))
2928 self.assertEqual('onetwo', f.format(self.records))
2929
2930 def test_custom(self):
2931 f = TestBufferingFormatter()
2932 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2933 lf = logging.Formatter('<%(message)s>')
2934 f = TestBufferingFormatter(lf)
2935 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002936
2937class ExceptionTest(BaseTest):
2938 def test_formatting(self):
2939 r = self.root_logger
2940 h = RecordingHandler()
2941 r.addHandler(h)
2942 try:
2943 raise RuntimeError('deliberate mistake')
2944 except:
2945 logging.exception('failed', stack_info=True)
2946 r.removeHandler(h)
2947 h.close()
2948 r = h.records[0]
2949 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2950 'call last):\n'))
2951 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2952 'deliberate mistake'))
2953 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2954 'call last):\n'))
2955 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2956 'stack_info=True)'))
2957
2958
Vinay Sajip5a27d402010-12-10 11:42:57 +00002959class LastResortTest(BaseTest):
2960 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002961 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002962 root = self.root_logger
2963 root.removeHandler(self.root_hdlr)
2964 old_stderr = sys.stderr
2965 old_lastresort = logging.lastResort
2966 old_raise_exceptions = logging.raiseExceptions
2967 try:
2968 sys.stderr = sio = io.StringIO()
Vinay Sajip8188f582011-07-25 19:58:13 +01002969 root.debug('This should not appear')
2970 self.assertEqual(sio.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00002971 root.warning('This is your final chance!')
2972 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2973 #No handlers and no last resort, so 'No handlers' message
2974 logging.lastResort = None
2975 sys.stderr = sio = io.StringIO()
2976 root.warning('This is your final chance!')
2977 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2978 # 'No handlers' message only printed once
2979 sys.stderr = sio = io.StringIO()
2980 root.warning('This is your final chance!')
2981 self.assertEqual(sio.getvalue(), '')
2982 root.manager.emittedNoHandlerWarning = False
2983 #If raiseExceptions is False, no message is printed
2984 logging.raiseExceptions = False
2985 sys.stderr = sio = io.StringIO()
2986 root.warning('This is your final chance!')
2987 self.assertEqual(sio.getvalue(), '')
2988 finally:
2989 sys.stderr = old_stderr
2990 root.addHandler(self.root_hdlr)
2991 logging.lastResort = old_lastresort
2992 logging.raiseExceptions = old_raise_exceptions
2993
2994
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002995class FakeHandler:
2996
2997 def __init__(self, identifier, called):
2998 for method in ('acquire', 'flush', 'close', 'release'):
2999 setattr(self, method, self.record_call(identifier, method, called))
3000
3001 def record_call(self, identifier, method_name, called):
3002 def inner():
3003 called.append('{} - {}'.format(identifier, method_name))
3004 return inner
3005
3006
3007class RecordingHandler(logging.NullHandler):
3008
3009 def __init__(self, *args, **kwargs):
3010 super(RecordingHandler, self).__init__(*args, **kwargs)
3011 self.records = []
3012
3013 def handle(self, record):
3014 """Keep track of all the emitted records."""
3015 self.records.append(record)
3016
3017
3018class ShutdownTest(BaseTest):
3019
Vinay Sajip5e66b162011-04-20 15:41:14 +01003020 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003021
3022 def setUp(self):
3023 super(ShutdownTest, self).setUp()
3024 self.called = []
3025
3026 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003027 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003028
3029 def raise_error(self, error):
3030 def inner():
3031 raise error()
3032 return inner
3033
3034 def test_no_failure(self):
3035 # create some fake handlers
3036 handler0 = FakeHandler(0, self.called)
3037 handler1 = FakeHandler(1, self.called)
3038 handler2 = FakeHandler(2, self.called)
3039
3040 # create live weakref to those handlers
3041 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
3042
3043 logging.shutdown(handlerList=list(handlers))
3044
3045 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
3046 '1 - acquire', '1 - flush', '1 - close', '1 - release',
3047 '0 - acquire', '0 - flush', '0 - close', '0 - release']
3048 self.assertEqual(expected, self.called)
3049
3050 def _test_with_failure_in_method(self, method, error):
3051 handler = FakeHandler(0, self.called)
3052 setattr(handler, method, self.raise_error(error))
3053 handlers = [logging.weakref.ref(handler)]
3054
3055 logging.shutdown(handlerList=list(handlers))
3056
3057 self.assertEqual('0 - release', self.called[-1])
3058
3059 def test_with_ioerror_in_acquire(self):
3060 self._test_with_failure_in_method('acquire', IOError)
3061
3062 def test_with_ioerror_in_flush(self):
3063 self._test_with_failure_in_method('flush', IOError)
3064
3065 def test_with_ioerror_in_close(self):
3066 self._test_with_failure_in_method('close', IOError)
3067
3068 def test_with_valueerror_in_acquire(self):
3069 self._test_with_failure_in_method('acquire', ValueError)
3070
3071 def test_with_valueerror_in_flush(self):
3072 self._test_with_failure_in_method('flush', ValueError)
3073
3074 def test_with_valueerror_in_close(self):
3075 self._test_with_failure_in_method('close', ValueError)
3076
3077 def test_with_other_error_in_acquire_without_raise(self):
3078 logging.raiseExceptions = False
3079 self._test_with_failure_in_method('acquire', IndexError)
3080
3081 def test_with_other_error_in_flush_without_raise(self):
3082 logging.raiseExceptions = False
3083 self._test_with_failure_in_method('flush', IndexError)
3084
3085 def test_with_other_error_in_close_without_raise(self):
3086 logging.raiseExceptions = False
3087 self._test_with_failure_in_method('close', IndexError)
3088
3089 def test_with_other_error_in_acquire_with_raise(self):
3090 logging.raiseExceptions = True
3091 self.assertRaises(IndexError, self._test_with_failure_in_method,
3092 'acquire', IndexError)
3093
3094 def test_with_other_error_in_flush_with_raise(self):
3095 logging.raiseExceptions = True
3096 self.assertRaises(IndexError, self._test_with_failure_in_method,
3097 'flush', IndexError)
3098
3099 def test_with_other_error_in_close_with_raise(self):
3100 logging.raiseExceptions = True
3101 self.assertRaises(IndexError, self._test_with_failure_in_method,
3102 'close', IndexError)
3103
3104
3105class ModuleLevelMiscTest(BaseTest):
3106
Vinay Sajip5e66b162011-04-20 15:41:14 +01003107 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003108
3109 def test_disable(self):
3110 old_disable = logging.root.manager.disable
3111 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003112 self.assertEqual(old_disable, 0)
3113 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003114
3115 logging.disable(83)
3116 self.assertEqual(logging.root.manager.disable, 83)
3117
3118 def _test_log(self, method, level=None):
3119 called = []
3120 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003121 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003122
3123 recording = RecordingHandler()
3124 logging.root.addHandler(recording)
3125
3126 log_method = getattr(logging, method)
3127 if level is not None:
3128 log_method(level, "test me: %r", recording)
3129 else:
3130 log_method("test me: %r", recording)
3131
3132 self.assertEqual(len(recording.records), 1)
3133 record = recording.records[0]
3134 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3135
3136 expected_level = level if level is not None else getattr(logging, method.upper())
3137 self.assertEqual(record.levelno, expected_level)
3138
3139 # basicConfig was not called!
3140 self.assertEqual(called, [])
3141
3142 def test_log(self):
3143 self._test_log('log', logging.ERROR)
3144
3145 def test_debug(self):
3146 self._test_log('debug')
3147
3148 def test_info(self):
3149 self._test_log('info')
3150
3151 def test_warning(self):
3152 self._test_log('warning')
3153
3154 def test_error(self):
3155 self._test_log('error')
3156
3157 def test_critical(self):
3158 self._test_log('critical')
3159
3160 def test_set_logger_class(self):
3161 self.assertRaises(TypeError, logging.setLoggerClass, object)
3162
3163 class MyLogger(logging.Logger):
3164 pass
3165
3166 logging.setLoggerClass(MyLogger)
3167 self.assertEqual(logging.getLoggerClass(), MyLogger)
3168
3169 logging.setLoggerClass(logging.Logger)
3170 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3171
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003172class LogRecordTest(BaseTest):
3173 def test_str_rep(self):
3174 r = logging.makeLogRecord({})
3175 s = str(r)
3176 self.assertTrue(s.startswith('<LogRecord: '))
3177 self.assertTrue(s.endswith('>'))
3178
3179 def test_dict_arg(self):
3180 h = RecordingHandler()
3181 r = logging.getLogger()
3182 r.addHandler(h)
3183 d = {'less' : 'more' }
3184 logging.warning('less is %(less)s', d)
3185 self.assertIs(h.records[0].args, d)
3186 self.assertEqual(h.records[0].message, 'less is more')
3187 r.removeHandler(h)
3188 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003189
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003190 def test_multiprocessing(self):
3191 r = logging.makeLogRecord({})
3192 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003193 try:
3194 import multiprocessing as mp
3195 r = logging.makeLogRecord({})
3196 self.assertEqual(r.processName, mp.current_process().name)
3197 except ImportError:
3198 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003199
3200 def test_optional(self):
3201 r = logging.makeLogRecord({})
3202 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003203 if threading:
3204 NOT_NONE(r.thread)
3205 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003206 NOT_NONE(r.process)
3207 NOT_NONE(r.processName)
3208 log_threads = logging.logThreads
3209 log_processes = logging.logProcesses
3210 log_multiprocessing = logging.logMultiprocessing
3211 try:
3212 logging.logThreads = False
3213 logging.logProcesses = False
3214 logging.logMultiprocessing = False
3215 r = logging.makeLogRecord({})
3216 NONE = self.assertIsNone
3217 NONE(r.thread)
3218 NONE(r.threadName)
3219 NONE(r.process)
3220 NONE(r.processName)
3221 finally:
3222 logging.logThreads = log_threads
3223 logging.logProcesses = log_processes
3224 logging.logMultiprocessing = log_multiprocessing
3225
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003226class BasicConfigTest(unittest.TestCase):
3227
Vinay Sajip95bf5042011-04-20 11:50:56 +01003228 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003229
3230 def setUp(self):
3231 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003232 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003233 self.saved_handlers = logging._handlers.copy()
3234 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003235 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003236 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003237 logging.root.handlers = []
3238
3239 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003240 for h in logging.root.handlers[:]:
3241 logging.root.removeHandler(h)
3242 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003243 super(BasicConfigTest, self).tearDown()
3244
Vinay Sajip3def7e02011-04-20 10:58:06 +01003245 def cleanup(self):
3246 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003247 logging._handlers.clear()
3248 logging._handlers.update(self.saved_handlers)
3249 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003250 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003251
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003252 def test_no_kwargs(self):
3253 logging.basicConfig()
3254
3255 # handler defaults to a StreamHandler to sys.stderr
3256 self.assertEqual(len(logging.root.handlers), 1)
3257 handler = logging.root.handlers[0]
3258 self.assertIsInstance(handler, logging.StreamHandler)
3259 self.assertEqual(handler.stream, sys.stderr)
3260
3261 formatter = handler.formatter
3262 # format defaults to logging.BASIC_FORMAT
3263 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3264 # datefmt defaults to None
3265 self.assertIsNone(formatter.datefmt)
3266 # style defaults to %
3267 self.assertIsInstance(formatter._style, logging.PercentStyle)
3268
Florent Xiclunae41f0de2011-11-11 19:39:25 +01003269 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003270 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003271
3272 def test_filename(self):
3273 logging.basicConfig(filename='test.log')
3274
3275 self.assertEqual(len(logging.root.handlers), 1)
3276 handler = logging.root.handlers[0]
3277 self.assertIsInstance(handler, logging.FileHandler)
3278
3279 expected = logging.FileHandler('test.log', 'a')
3280 self.addCleanup(expected.close)
3281 self.assertEqual(handler.stream.mode, expected.stream.mode)
3282 self.assertEqual(handler.stream.name, expected.stream.name)
3283
3284 def test_filemode(self):
3285 logging.basicConfig(filename='test.log', filemode='wb')
3286
3287 handler = logging.root.handlers[0]
3288 expected = logging.FileHandler('test.log', 'wb')
3289 self.addCleanup(expected.close)
3290 self.assertEqual(handler.stream.mode, expected.stream.mode)
3291
3292 def test_stream(self):
3293 stream = io.StringIO()
3294 self.addCleanup(stream.close)
3295 logging.basicConfig(stream=stream)
3296
3297 self.assertEqual(len(logging.root.handlers), 1)
3298 handler = logging.root.handlers[0]
3299 self.assertIsInstance(handler, logging.StreamHandler)
3300 self.assertEqual(handler.stream, stream)
3301
3302 def test_format(self):
3303 logging.basicConfig(format='foo')
3304
3305 formatter = logging.root.handlers[0].formatter
3306 self.assertEqual(formatter._style._fmt, 'foo')
3307
3308 def test_datefmt(self):
3309 logging.basicConfig(datefmt='bar')
3310
3311 formatter = logging.root.handlers[0].formatter
3312 self.assertEqual(formatter.datefmt, 'bar')
3313
3314 def test_style(self):
3315 logging.basicConfig(style='$')
3316
3317 formatter = logging.root.handlers[0].formatter
3318 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3319
3320 def test_level(self):
3321 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003322 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003323
3324 logging.basicConfig(level=57)
3325 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003326 # Test that second call has no effect
3327 logging.basicConfig(level=58)
3328 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003329
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003330 def test_incompatible(self):
3331 assertRaises = self.assertRaises
3332 handlers = [logging.StreamHandler()]
3333 stream = sys.stderr
3334 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3335 stream=stream)
3336 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3337 handlers=handlers)
3338 assertRaises(ValueError, logging.basicConfig, stream=stream,
3339 handlers=handlers)
3340
3341 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003342 handlers = [
3343 logging.StreamHandler(),
3344 logging.StreamHandler(sys.stdout),
3345 logging.StreamHandler(),
3346 ]
3347 f = logging.Formatter()
3348 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003349 logging.basicConfig(handlers=handlers)
3350 self.assertIs(handlers[0], logging.root.handlers[0])
3351 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003352 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003353 self.assertIsNotNone(handlers[0].formatter)
3354 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003355 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003356 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3357
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003358 def _test_log(self, method, level=None):
3359 # logging.root has no handlers so basicConfig should be called
3360 called = []
3361
3362 old_basic_config = logging.basicConfig
3363 def my_basic_config(*a, **kw):
3364 old_basic_config()
3365 old_level = logging.root.level
3366 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003367 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003368 called.append((a, kw))
3369
3370 patch(self, logging, 'basicConfig', my_basic_config)
3371
3372 log_method = getattr(logging, method)
3373 if level is not None:
3374 log_method(level, "test me")
3375 else:
3376 log_method("test me")
3377
3378 # basicConfig was called with no arguments
3379 self.assertEqual(called, [((), {})])
3380
3381 def test_log(self):
3382 self._test_log('log', logging.WARNING)
3383
3384 def test_debug(self):
3385 self._test_log('debug')
3386
3387 def test_info(self):
3388 self._test_log('info')
3389
3390 def test_warning(self):
3391 self._test_log('warning')
3392
3393 def test_error(self):
3394 self._test_log('error')
3395
3396 def test_critical(self):
3397 self._test_log('critical')
3398
3399
3400class LoggerAdapterTest(unittest.TestCase):
3401
3402 def setUp(self):
3403 super(LoggerAdapterTest, self).setUp()
3404 old_handler_list = logging._handlerList[:]
3405
3406 self.recording = RecordingHandler()
3407 self.logger = logging.root
3408 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003409 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003410 self.addCleanup(self.recording.close)
3411
3412 def cleanup():
3413 logging._handlerList[:] = old_handler_list
3414
3415 self.addCleanup(cleanup)
3416 self.addCleanup(logging.shutdown)
3417 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3418
3419 def test_exception(self):
3420 msg = 'testing exception: %r'
3421 exc = None
3422 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003423 1 / 0
3424 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003425 exc = e
3426 self.adapter.exception(msg, self.recording)
3427
3428 self.assertEqual(len(self.recording.records), 1)
3429 record = self.recording.records[0]
3430 self.assertEqual(record.levelno, logging.ERROR)
3431 self.assertEqual(record.msg, msg)
3432 self.assertEqual(record.args, (self.recording,))
3433 self.assertEqual(record.exc_info,
3434 (exc.__class__, exc, exc.__traceback__))
3435
3436 def test_critical(self):
3437 msg = 'critical test! %r'
3438 self.adapter.critical(msg, self.recording)
3439
3440 self.assertEqual(len(self.recording.records), 1)
3441 record = self.recording.records[0]
3442 self.assertEqual(record.levelno, logging.CRITICAL)
3443 self.assertEqual(record.msg, msg)
3444 self.assertEqual(record.args, (self.recording,))
3445
3446 def test_is_enabled_for(self):
3447 old_disable = self.adapter.logger.manager.disable
3448 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003449 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3450 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003451 self.assertFalse(self.adapter.isEnabledFor(32))
3452
3453 def test_has_handlers(self):
3454 self.assertTrue(self.adapter.hasHandlers())
3455
3456 for handler in self.logger.handlers:
3457 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003458
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003459 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003460 self.assertFalse(self.adapter.hasHandlers())
3461
3462
3463class LoggerTest(BaseTest):
3464
3465 def setUp(self):
3466 super(LoggerTest, self).setUp()
3467 self.recording = RecordingHandler()
3468 self.logger = logging.Logger(name='blah')
3469 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003470 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003471 self.addCleanup(self.recording.close)
3472 self.addCleanup(logging.shutdown)
3473
3474 def test_set_invalid_level(self):
3475 self.assertRaises(TypeError, self.logger.setLevel, object())
3476
3477 def test_exception(self):
3478 msg = 'testing exception: %r'
3479 exc = None
3480 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003481 1 / 0
3482 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003483 exc = e
3484 self.logger.exception(msg, self.recording)
3485
3486 self.assertEqual(len(self.recording.records), 1)
3487 record = self.recording.records[0]
3488 self.assertEqual(record.levelno, logging.ERROR)
3489 self.assertEqual(record.msg, msg)
3490 self.assertEqual(record.args, (self.recording,))
3491 self.assertEqual(record.exc_info,
3492 (exc.__class__, exc, exc.__traceback__))
3493
3494 def test_log_invalid_level_with_raise(self):
3495 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003496 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003497
3498 logging.raiseExceptions = True
3499 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3500
3501 def test_log_invalid_level_no_raise(self):
3502 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003503 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003504
3505 logging.raiseExceptions = False
3506 self.logger.log('10', 'test message') # no exception happens
3507
3508 def test_find_caller_with_stack_info(self):
3509 called = []
3510 patch(self, logging.traceback, 'print_stack',
3511 lambda f, file: called.append(file.getvalue()))
3512
3513 self.logger.findCaller(stack_info=True)
3514
3515 self.assertEqual(len(called), 1)
3516 self.assertEqual('Stack (most recent call last):\n', called[0])
3517
3518 def test_make_record_with_extra_overwrite(self):
3519 name = 'my record'
3520 level = 13
3521 fn = lno = msg = args = exc_info = func = sinfo = None
3522 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3523 exc_info, func, sinfo)
3524
3525 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3526 extra = {key: 'some value'}
3527 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3528 fn, lno, msg, args, exc_info,
3529 extra=extra, sinfo=sinfo)
3530
3531 def test_make_record_with_extra_no_overwrite(self):
3532 name = 'my record'
3533 level = 13
3534 fn = lno = msg = args = exc_info = func = sinfo = None
3535 extra = {'valid_key': 'some value'}
3536 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3537 exc_info, extra=extra, sinfo=sinfo)
3538 self.assertIn('valid_key', result.__dict__)
3539
3540 def test_has_handlers(self):
3541 self.assertTrue(self.logger.hasHandlers())
3542
3543 for handler in self.logger.handlers:
3544 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003545 self.assertFalse(self.logger.hasHandlers())
3546
3547 def test_has_handlers_no_propagate(self):
3548 child_logger = logging.getLogger('blah.child')
3549 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003550 self.assertFalse(child_logger.hasHandlers())
3551
3552 def test_is_enabled_for(self):
3553 old_disable = self.logger.manager.disable
3554 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003555 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003556 self.assertFalse(self.logger.isEnabledFor(22))
3557
Florent Xicluna5252f9f2011-11-07 19:43:05 +01003558 def test_root_logger_aliases(self):
3559 root = logging.getLogger()
3560 self.assertIs(root, logging.root)
3561 self.assertIs(root, logging.getLogger(None))
3562 self.assertIs(root, logging.getLogger(''))
3563 self.assertIs(root, logging.getLogger('foo').root)
3564 self.assertIs(root, logging.getLogger('foo.bar').root)
3565 self.assertIs(root, logging.getLogger('foo').parent)
3566
3567 self.assertIsNot(root, logging.getLogger('\0'))
3568 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
3569
3570 def test_invalid_names(self):
3571 self.assertRaises(TypeError, logging.getLogger, any)
3572 self.assertRaises(TypeError, logging.getLogger, b'foo')
3573
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003574
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003575class BaseFileTest(BaseTest):
3576 "Base class for handler tests that write log files"
3577
3578 def setUp(self):
3579 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003580 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3581 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003582 self.rmfiles = []
3583
3584 def tearDown(self):
3585 for fn in self.rmfiles:
3586 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003587 if os.path.exists(self.fn):
3588 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003589 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003590
3591 def assertLogFile(self, filename):
3592 "Assert a log file is there and register it for deletion"
3593 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003594 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003595 self.rmfiles.append(filename)
3596
3597
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003598class FileHandlerTest(BaseFileTest):
3599 def test_delay(self):
3600 os.unlink(self.fn)
3601 fh = logging.FileHandler(self.fn, delay=True)
3602 self.assertIsNone(fh.stream)
3603 self.assertFalse(os.path.exists(self.fn))
3604 fh.handle(logging.makeLogRecord({}))
3605 self.assertIsNotNone(fh.stream)
3606 self.assertTrue(os.path.exists(self.fn))
3607 fh.close()
3608
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003609class RotatingFileHandlerTest(BaseFileTest):
3610 def next_rec(self):
3611 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3612 self.next_message(), None, None, None)
3613
3614 def test_should_not_rollover(self):
3615 # If maxbytes is zero rollover never occurs
3616 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3617 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003618 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003619
3620 def test_should_rollover(self):
3621 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3622 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003623 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003624
3625 def test_file_created(self):
3626 # checks that the file is created and assumes it was created
3627 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003628 rh = logging.handlers.RotatingFileHandler(self.fn)
3629 rh.emit(self.next_rec())
3630 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003631 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003632
3633 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00003634 def namer(name):
3635 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003636 rh = logging.handlers.RotatingFileHandler(
3637 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003638 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003639 rh.emit(self.next_rec())
3640 self.assertLogFile(self.fn)
3641 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003642 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003643 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003644 self.assertLogFile(namer(self.fn + ".2"))
3645 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
3646 rh.close()
3647
Eric V. Smith851cad72012-03-11 22:46:04 -07003648 @requires_zlib
Vinay Sajip23b94d02012-01-04 12:02:26 +00003649 def test_rotator(self):
3650 def namer(name):
3651 return name + ".gz"
3652
3653 def rotator(source, dest):
3654 with open(source, "rb") as sf:
3655 data = sf.read()
3656 compressed = zlib.compress(data, 9)
3657 with open(dest, "wb") as df:
3658 df.write(compressed)
3659 os.remove(source)
3660
3661 rh = logging.handlers.RotatingFileHandler(
3662 self.fn, backupCount=2, maxBytes=1)
3663 rh.rotator = rotator
3664 rh.namer = namer
3665 m1 = self.next_rec()
3666 rh.emit(m1)
3667 self.assertLogFile(self.fn)
3668 m2 = self.next_rec()
3669 rh.emit(m2)
3670 fn = namer(self.fn + ".1")
3671 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003672 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00003673 with open(fn, "rb") as f:
3674 compressed = f.read()
3675 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003676 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003677 rh.emit(self.next_rec())
3678 fn = namer(self.fn + ".2")
3679 self.assertLogFile(fn)
3680 with open(fn, "rb") as f:
3681 compressed = f.read()
3682 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003683 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003684 rh.emit(self.next_rec())
3685 fn = namer(self.fn + ".2")
3686 with open(fn, "rb") as f:
3687 compressed = f.read()
3688 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003689 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003690 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00003691 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003692
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003693class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003694 # other test methods added below
3695 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003696 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3697 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00003698 fmt = logging.Formatter('%(asctime)s %(message)s')
3699 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003700 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003701 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003702 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003703 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00003704 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003705 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01003706 fh.close()
3707 # At this point, we should have a recent rotated file which we
3708 # can test for the existence of. However, in practice, on some
3709 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003710 # in time to go to look for the log file. So, we go back a fair
3711 # bit, and stop as soon as we see a rotated file. In theory this
3712 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003713 found = False
3714 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003715 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003716 for secs in range(GO_BACK):
3717 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003718 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3719 found = os.path.exists(fn)
3720 if found:
3721 self.rmfiles.append(fn)
3722 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003723 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3724 if not found:
3725 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003726 dn, fn = os.path.split(self.fn)
3727 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003728 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3729 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00003730 for f in files:
3731 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00003732 path = os.path.join(dn, f)
3733 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00003734 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01003735 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003736
Vinay Sajip0372e102011-05-05 12:59:14 +01003737 def test_invalid(self):
3738 assertRaises = self.assertRaises
3739 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003740 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003741 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003742 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003743 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003744 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003745
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003746def secs(**kw):
3747 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3748
3749for when, exp in (('S', 1),
3750 ('M', 60),
3751 ('H', 60 * 60),
3752 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003753 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003754 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003755 ('W0', secs(days=4, hours=24)),
3756 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003757 def test_compute_rollover(self, when=when, exp=exp):
3758 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003759 self.fn, when=when, interval=1, backupCount=0, utc=True)
3760 currentTime = 0.0
3761 actual = rh.computeRollover(currentTime)
3762 if exp != actual:
3763 # Failures occur on some systems for MIDNIGHT and W0.
3764 # Print detailed calculation for MIDNIGHT so we can try to see
3765 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00003766 if when == 'MIDNIGHT':
3767 try:
3768 if rh.utc:
3769 t = time.gmtime(currentTime)
3770 else:
3771 t = time.localtime(currentTime)
3772 currentHour = t[3]
3773 currentMinute = t[4]
3774 currentSecond = t[5]
3775 # r is the number of seconds left between now and midnight
3776 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3777 currentMinute) * 60 +
3778 currentSecond)
3779 result = currentTime + r
3780 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3781 print('currentHour: %s' % currentHour, file=sys.stderr)
3782 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3783 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3784 print('r: %s' % r, file=sys.stderr)
3785 print('result: %s' % result, file=sys.stderr)
3786 except Exception:
3787 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3788 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003789 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003790 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3791
Vinay Sajip60ccd822011-05-09 17:32:09 +01003792
3793@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3794class NTEventLogHandlerTest(BaseTest):
3795 def test_basic(self):
3796 logtype = 'Application'
3797 elh = win32evtlog.OpenEventLog(None, logtype)
3798 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3799 h = logging.handlers.NTEventLogHandler('test_logging')
3800 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3801 h.handle(r)
3802 h.close()
3803 # Now see if the event is recorded
3804 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3805 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3806 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3807 found = False
3808 GO_BACK = 100
3809 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3810 for e in events:
3811 if e.SourceName != 'test_logging':
3812 continue
3813 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3814 if msg != 'Test Log Message\r\n':
3815 continue
3816 found = True
3817 break
3818 msg = 'Record not found in event log, went back %d records' % GO_BACK
3819 self.assertTrue(found, msg=msg)
3820
Christian Heimes180510d2008-03-03 19:15:45 +00003821# Set the locale to the platform-dependent default. I have no idea
3822# why the test does this, but in any case we save the current locale
3823# first and restore it at the end.
3824@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003825def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003826 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003827 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003828 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3829 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3830 ManagerTest, FormatterTest, BufferingFormatterTest,
3831 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3832 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3833 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3834 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003835 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003836 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003837 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003838 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003839
Christian Heimes180510d2008-03-03 19:15:45 +00003840if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003841 test_main()