blob: c9a051ae9f5725a3d9796fd2594bf4c2c89cf2a7 [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajip3f885b52013-03-22 15:19:54 +00003# Copyright 2001-2013 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 Sajip3f885b52013-03-22 15:19:54 +000021Copyright (C) 2001-2013 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 Sajip5e86eed2012-05-29 22:48:10 +0100585 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100586 @unittest.skipUnless(threading, 'Threading required for this test.')
587 def test_race(self):
588 # Issue #14632 refers.
589 def remove_loop(fname, tries):
590 for _ in range(tries):
591 try:
592 os.unlink(fname)
593 except OSError:
594 pass
595 time.sleep(0.004 * random.randint(0, 4))
596
Vinay Sajipc94871a2012-04-25 10:51:35 +0100597 del_count = 500
598 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100599
Vinay Sajipa5798de2012-04-24 23:33:33 +0100600 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100601 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
602 os.close(fd)
603 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
604 remover.daemon = True
605 remover.start()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100606 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100607 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
608 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100609 try:
610 for _ in range(log_count):
611 time.sleep(0.005)
612 r = logging.makeLogRecord({'msg': 'testing' })
613 h.handle(r)
614 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100615 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100616 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100617 if os.path.exists(fn):
618 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100619
620
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100621class BadStream(object):
622 def write(self, data):
623 raise RuntimeError('deliberate mistake')
624
625class TestStreamHandler(logging.StreamHandler):
626 def handleError(self, record):
627 self.error_record = record
628
629class StreamHandlerTest(BaseTest):
630 def test_error_handling(self):
631 h = TestStreamHandler(BadStream())
632 r = logging.makeLogRecord({})
633 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100634 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100635 try:
636 h.handle(r)
637 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100638 h = logging.StreamHandler(BadStream())
639 sys.stderr = sio = io.StringIO()
640 h.handle(r)
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100641 self.assertIn('\nRuntimeError: deliberate mistake\n',
642 sio.getvalue())
Vinay Sajip985ef872011-04-26 19:34:04 +0100643 logging.raiseExceptions = False
644 sys.stderr = sio = io.StringIO()
645 h.handle(r)
646 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100647 finally:
648 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100649 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100650
Vinay Sajip7367d082011-05-02 13:17:27 +0100651# -- The following section could be moved into a server_helper.py module
652# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100653
Vinay Sajipce7c9782011-05-17 07:15:53 +0100654if threading:
655 class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100656 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100657 This derived class has had to be created because smtpd does not
658 support use of custom channel maps, although they are allowed by
659 asyncore's design. Issue #11959 has been raised to address this,
660 and if resolved satisfactorily, some of this code can be removed.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100661 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100662 def __init__(self, server, conn, addr, sockmap):
663 asynchat.async_chat.__init__(self, conn, sockmap)
664 self.smtp_server = server
665 self.conn = conn
666 self.addr = addr
R David Murrayd1a30c92012-05-26 14:33:59 -0400667 self.data_size_limit = None
Vinay Sajipce7c9782011-05-17 07:15:53 +0100668 self.received_lines = []
669 self.smtp_state = self.COMMAND
670 self.seen_greeting = ''
671 self.mailfrom = None
672 self.rcpttos = []
673 self.received_data = ''
674 self.fqdn = socket.getfqdn()
675 self.num_bytes = 0
676 try:
677 self.peer = conn.getpeername()
678 except socket.error as err:
679 # a race condition may occur if the other end is closing
680 # before we can get the peername
681 self.close()
682 if err.args[0] != errno.ENOTCONN:
683 raise
684 return
685 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
686 self.set_terminator(b'\r\n')
R David Murrayd1a30c92012-05-26 14:33:59 -0400687 self.extended_smtp = False
Vinay Sajipa463d252011-04-30 21:52:48 +0100688
Vinay Sajip314b92b2011-05-02 14:31:16 +0100689
Vinay Sajipce7c9782011-05-17 07:15:53 +0100690 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100691 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100692 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100693
Vinay Sajipce7c9782011-05-17 07:15:53 +0100694 :param addr: A (host, port) tuple which the server listens on.
695 You can specify a port value of zero: the server's
696 *port* attribute will hold the actual port number
697 used, which can be used in client connections.
698 :param handler: A callable which will be called to process
699 incoming messages. The handler will be passed
700 the client address tuple, who the message is from,
701 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100702 :param poll_interval: The interval, in seconds, used in the underlying
703 :func:`select` or :func:`poll` call by
704 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100705 :param sockmap: A dictionary which will be used to hold
706 :class:`asyncore.dispatcher` instances used by
707 :func:`asyncore.loop`. This avoids changing the
708 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100709 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100710 channel_class = TestSMTPChannel
711
712 def __init__(self, addr, handler, poll_interval, sockmap):
713 self._localaddr = addr
714 self._remoteaddr = None
R David Murrayd1a30c92012-05-26 14:33:59 -0400715 self.data_size_limit = None
Vinay Sajipce7c9782011-05-17 07:15:53 +0100716 self.sockmap = sockmap
717 asyncore.dispatcher.__init__(self, map=sockmap)
718 try:
719 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
720 sock.setblocking(0)
721 self.set_socket(sock, map=sockmap)
722 # try to re-use a server port if possible
723 self.set_reuse_addr()
724 self.bind(addr)
725 self.port = sock.getsockname()[1]
726 self.listen(5)
727 except:
728 self.close()
Vinay Sajipe73afad2011-05-10 07:48:28 +0100729 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +0100730 self._handler = handler
731 self._thread = None
732 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100733
Vinay Sajipce7c9782011-05-17 07:15:53 +0100734 def handle_accepted(self, conn, addr):
735 """
736 Redefined only because the base class does not pass in a
737 map, forcing use of a global in :mod:`asyncore`.
738 """
739 channel = self.channel_class(self, conn, addr, self.sockmap)
Vinay Sajip314b92b2011-05-02 14:31:16 +0100740
Vinay Sajipce7c9782011-05-17 07:15:53 +0100741 def process_message(self, peer, mailfrom, rcpttos, data):
742 """
743 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100744
Vinay Sajipce7c9782011-05-17 07:15:53 +0100745 Typically, this will be a test case method.
746 :param peer: The client (host, port) tuple.
747 :param mailfrom: The address of the sender.
748 :param rcpttos: The addresses of the recipients.
749 :param data: The message.
750 """
751 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100752
Vinay Sajipce7c9782011-05-17 07:15:53 +0100753 def start(self):
754 """
755 Start the server running on a separate daemon thread.
756 """
757 self._thread = t = threading.Thread(target=self.serve_forever,
758 args=(self.poll_interval,))
759 t.setDaemon(True)
760 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100761
Vinay Sajipce7c9782011-05-17 07:15:53 +0100762 def serve_forever(self, poll_interval):
763 """
764 Run the :mod:`asyncore` loop until normal termination
765 conditions arise.
766 :param poll_interval: The interval, in seconds, used in the underlying
767 :func:`select` or :func:`poll` call by
768 :func:`asyncore.loop`.
769 """
770 try:
771 asyncore.loop(poll_interval, map=self.sockmap)
772 except select.error:
773 # On FreeBSD 8, closing the server repeatably
774 # raises this error. We swallow it if the
775 # server has been closed.
776 if self.connected or self.accepting:
777 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100778
Vinay Sajipce7c9782011-05-17 07:15:53 +0100779 def stop(self, timeout=None):
780 """
781 Stop the thread by closing the server instance.
782 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100783
Vinay Sajipce7c9782011-05-17 07:15:53 +0100784 :param timeout: How long to wait for the server thread
785 to terminate.
786 """
787 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100788 self._thread.join(timeout)
789 self._thread = None
Vinay Sajip7367d082011-05-02 13:17:27 +0100790
Vinay Sajipce7c9782011-05-17 07:15:53 +0100791 class ControlMixin(object):
792 """
793 This mixin is used to start a server on a separate thread, and
794 shut it down programmatically. Request handling is simplified - instead
795 of needing to derive a suitable RequestHandler subclass, you just
796 provide a callable which will be passed each received request to be
797 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100798
Vinay Sajipce7c9782011-05-17 07:15:53 +0100799 :param handler: A handler callable which will be called with a
800 single parameter - the request - in order to
801 process the request. This handler is called on the
802 server thread, effectively meaning that requests are
803 processed serially. While not quite Web scale ;-),
804 this should be fine for testing applications.
805 :param poll_interval: The polling interval in seconds.
806 """
807 def __init__(self, handler, poll_interval):
808 self._thread = None
809 self.poll_interval = poll_interval
810 self._handler = handler
811 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100812
Vinay Sajipce7c9782011-05-17 07:15:53 +0100813 def start(self):
814 """
815 Create a daemon thread to run the server, and start it.
816 """
817 self._thread = t = threading.Thread(target=self.serve_forever,
818 args=(self.poll_interval,))
819 t.setDaemon(True)
820 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100821
Vinay Sajipce7c9782011-05-17 07:15:53 +0100822 def serve_forever(self, poll_interval):
823 """
824 Run the server. Set the ready flag before entering the
825 service loop.
826 """
827 self.ready.set()
828 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100829
Vinay Sajipce7c9782011-05-17 07:15:53 +0100830 def stop(self, timeout=None):
831 """
832 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100833
Vinay Sajipce7c9782011-05-17 07:15:53 +0100834 :param timeout: How long to wait for the server thread
835 to terminate.
836 """
837 self.shutdown()
838 if self._thread is not None:
839 self._thread.join(timeout)
840 self._thread = None
841 self.server_close()
842 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100843
Vinay Sajipce7c9782011-05-17 07:15:53 +0100844 class TestHTTPServer(ControlMixin, HTTPServer):
845 """
846 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100847
Vinay Sajipce7c9782011-05-17 07:15:53 +0100848 :param addr: A tuple with the IP address and port to listen on.
849 :param handler: A handler callable which will be called with a
850 single parameter - the request - in order to
851 process the request.
852 :param poll_interval: The polling interval in seconds.
853 :param log: Pass ``True`` to enable log messages.
854 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100855 def __init__(self, addr, handler, poll_interval=0.5,
856 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100857 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
858 def __getattr__(self, name, default=None):
859 if name.startswith('do_'):
860 return self.process_request
861 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100862
Vinay Sajipce7c9782011-05-17 07:15:53 +0100863 def process_request(self):
864 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100865
Vinay Sajipce7c9782011-05-17 07:15:53 +0100866 def log_message(self, format, *args):
867 if log:
868 super(DelegatingHTTPRequestHandler,
869 self).log_message(format, *args)
870 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
871 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100872 self.sslctx = sslctx
873
874 def get_request(self):
875 try:
876 sock, addr = self.socket.accept()
877 if self.sslctx:
878 sock = self.sslctx.wrap_socket(sock, server_side=True)
879 except socket.error as e:
880 # socket errors are silenced by the caller, print them here
881 sys.stderr.write("Got an error:\n%s\n" % e)
882 raise
883 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100884
Vinay Sajipce7c9782011-05-17 07:15:53 +0100885 class TestTCPServer(ControlMixin, ThreadingTCPServer):
886 """
887 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100888
Vinay Sajipce7c9782011-05-17 07:15:53 +0100889 :param addr: A tuple with the IP address and port to listen on.
890 :param handler: A handler callable which will be called with a single
891 parameter - the request - in order to process the request.
892 :param poll_interval: The polling interval in seconds.
893 :bind_and_activate: If True (the default), binds the server and starts it
894 listening. If False, you need to call
895 :meth:`server_bind` and :meth:`server_activate` at
896 some later time before calling :meth:`start`, so that
897 the server will set up the socket and listen on it.
898 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100899
Vinay Sajipce7c9782011-05-17 07:15:53 +0100900 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100901
Vinay Sajipce7c9782011-05-17 07:15:53 +0100902 def __init__(self, addr, handler, poll_interval=0.5,
903 bind_and_activate=True):
904 class DelegatingTCPRequestHandler(StreamRequestHandler):
905
906 def handle(self):
907 self.server._handler(self)
908 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
909 bind_and_activate)
910 ControlMixin.__init__(self, handler, poll_interval)
911
912 def server_bind(self):
913 super(TestTCPServer, self).server_bind()
914 self.port = self.socket.getsockname()[1]
915
916 class TestUDPServer(ControlMixin, ThreadingUDPServer):
917 """
918 A UDP server which is controllable using :class:`ControlMixin`.
919
920 :param addr: A tuple with the IP address and port to listen on.
921 :param handler: A handler callable which will be called with a
922 single parameter - the request - in order to
923 process the request.
924 :param poll_interval: The polling interval for shutdown requests,
925 in seconds.
926 :bind_and_activate: If True (the default), binds the server and
927 starts it listening. If False, you need to
928 call :meth:`server_bind` and
929 :meth:`server_activate` at some later time
930 before calling :meth:`start`, so that the server will
931 set up the socket and listen on it.
932 """
Vinay Sajip3ef12292011-05-23 23:00:42 +0100933 def __init__(self, addr, handler, poll_interval=0.5,
934 bind_and_activate=True):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100935 class DelegatingUDPRequestHandler(DatagramRequestHandler):
936
937 def handle(self):
938 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100939
940 def finish(self):
941 data = self.wfile.getvalue()
Vinay Sajip3ef12292011-05-23 23:00:42 +0100942 if data:
943 try:
944 super(DelegatingUDPRequestHandler, self).finish()
945 except socket.error:
946 if not self.server._closed:
947 raise
Vinay Sajipba980db2011-05-23 21:37:54 +0100948
Vinay Sajip3ef12292011-05-23 23:00:42 +0100949 ThreadingUDPServer.__init__(self, addr,
950 DelegatingUDPRequestHandler,
Vinay Sajipce7c9782011-05-17 07:15:53 +0100951 bind_and_activate)
952 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100953 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100954
955 def server_bind(self):
956 super(TestUDPServer, self).server_bind()
957 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100958
Vinay Sajipba980db2011-05-23 21:37:54 +0100959 def server_close(self):
960 super(TestUDPServer, self).server_close()
961 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100962
963# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100964
Vinay Sajipce7c9782011-05-17 07:15:53 +0100965@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100966class SMTPHandlerTest(BaseTest):
967 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100968 sockmap = {}
969 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
970 sockmap)
971 server.start()
972 addr = ('localhost', server.port)
Vinay Sajip4c3f4782012-04-22 18:19:50 +0100973 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log', timeout=5.0)
Vinay Sajipa463d252011-04-30 21:52:48 +0100974 self.assertEqual(h.toaddrs, ['you'])
975 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100976 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100977 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100978 h.handle(r)
Vinay Sajip17160fd2012-03-15 12:02:08 +0000979 self.handled.wait(5.0) # 14314: don't wait forever
Vinay Sajipa463d252011-04-30 21:52:48 +0100980 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +0000981 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +0100982 self.assertEqual(len(self.messages), 1)
983 peer, mailfrom, rcpttos, data = self.messages[0]
984 self.assertEqual(mailfrom, 'me')
985 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100986 self.assertIn('\nSubject: Log\n', data)
Vinay Sajipa463d252011-04-30 21:52:48 +0100987 self.assertTrue(data.endswith('\n\nHello'))
988 h.close()
989
990 def process_message(self, *args):
991 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100992 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100993
Christian Heimes180510d2008-03-03 19:15:45 +0000994class MemoryHandlerTest(BaseTest):
995
996 """Tests for the MemoryHandler."""
997
998 # Do not bother with a logger name group.
999 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
1000
1001 def setUp(self):
1002 BaseTest.setUp(self)
1003 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1004 self.root_hdlr)
1005 self.mem_logger = logging.getLogger('mem')
1006 self.mem_logger.propagate = 0
1007 self.mem_logger.addHandler(self.mem_hdlr)
1008
1009 def tearDown(self):
1010 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001011 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001012
1013 def test_flush(self):
1014 # The memory handler flushes to its target handler based on specific
1015 # criteria (message count and message level).
1016 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001017 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001018 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001019 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001020 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001021 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001022 lines = [
1023 ('DEBUG', '1'),
1024 ('INFO', '2'),
1025 ('WARNING', '3'),
1026 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001027 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001028 for n in (4, 14):
1029 for i in range(9):
1030 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001031 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001032 # This will flush because it's the 10th message since the last
1033 # flush.
1034 self.mem_logger.debug(self.next_message())
1035 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001036 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001037
1038 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001039 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001040
1041
1042class ExceptionFormatter(logging.Formatter):
1043 """A special exception formatter."""
1044 def formatException(self, ei):
1045 return "Got a [%s]" % ei[0].__name__
1046
1047
1048class ConfigFileTest(BaseTest):
1049
1050 """Reading logging config from a .ini-style config file."""
1051
1052 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1053
1054 # config0 is a standard configuration.
1055 config0 = """
1056 [loggers]
1057 keys=root
1058
1059 [handlers]
1060 keys=hand1
1061
1062 [formatters]
1063 keys=form1
1064
1065 [logger_root]
1066 level=WARNING
1067 handlers=hand1
1068
1069 [handler_hand1]
1070 class=StreamHandler
1071 level=NOTSET
1072 formatter=form1
1073 args=(sys.stdout,)
1074
1075 [formatter_form1]
1076 format=%(levelname)s ++ %(message)s
1077 datefmt=
1078 """
1079
1080 # config1 adds a little to the standard configuration.
1081 config1 = """
1082 [loggers]
1083 keys=root,parser
1084
1085 [handlers]
1086 keys=hand1
1087
1088 [formatters]
1089 keys=form1
1090
1091 [logger_root]
1092 level=WARNING
1093 handlers=
1094
1095 [logger_parser]
1096 level=DEBUG
1097 handlers=hand1
1098 propagate=1
1099 qualname=compiler.parser
1100
1101 [handler_hand1]
1102 class=StreamHandler
1103 level=NOTSET
1104 formatter=form1
1105 args=(sys.stdout,)
1106
1107 [formatter_form1]
1108 format=%(levelname)s ++ %(message)s
1109 datefmt=
1110 """
1111
Vinay Sajip3f84b072011-03-07 17:49:33 +00001112 # config1a moves the handler to the root.
1113 config1a = """
1114 [loggers]
1115 keys=root,parser
1116
1117 [handlers]
1118 keys=hand1
1119
1120 [formatters]
1121 keys=form1
1122
1123 [logger_root]
1124 level=WARNING
1125 handlers=hand1
1126
1127 [logger_parser]
1128 level=DEBUG
1129 handlers=
1130 propagate=1
1131 qualname=compiler.parser
1132
1133 [handler_hand1]
1134 class=StreamHandler
1135 level=NOTSET
1136 formatter=form1
1137 args=(sys.stdout,)
1138
1139 [formatter_form1]
1140 format=%(levelname)s ++ %(message)s
1141 datefmt=
1142 """
1143
Christian Heimes180510d2008-03-03 19:15:45 +00001144 # config2 has a subtle configuration error that should be reported
1145 config2 = config1.replace("sys.stdout", "sys.stbout")
1146
1147 # config3 has a less subtle configuration error
1148 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1149
1150 # config4 specifies a custom formatter class to be loaded
1151 config4 = """
1152 [loggers]
1153 keys=root
1154
1155 [handlers]
1156 keys=hand1
1157
1158 [formatters]
1159 keys=form1
1160
1161 [logger_root]
1162 level=NOTSET
1163 handlers=hand1
1164
1165 [handler_hand1]
1166 class=StreamHandler
1167 level=NOTSET
1168 formatter=form1
1169 args=(sys.stdout,)
1170
1171 [formatter_form1]
1172 class=""" + __name__ + """.ExceptionFormatter
1173 format=%(levelname)s:%(name)s:%(message)s
1174 datefmt=
1175 """
1176
Georg Brandl3dbca812008-07-23 16:10:53 +00001177 # config5 specifies a custom handler class to be loaded
1178 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1179
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001180 # config6 uses ', ' delimiters in the handlers and formatters sections
1181 config6 = """
1182 [loggers]
1183 keys=root,parser
1184
1185 [handlers]
1186 keys=hand1, hand2
1187
1188 [formatters]
1189 keys=form1, form2
1190
1191 [logger_root]
1192 level=WARNING
1193 handlers=
1194
1195 [logger_parser]
1196 level=DEBUG
1197 handlers=hand1
1198 propagate=1
1199 qualname=compiler.parser
1200
1201 [handler_hand1]
1202 class=StreamHandler
1203 level=NOTSET
1204 formatter=form1
1205 args=(sys.stdout,)
1206
1207 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001208 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001209 level=NOTSET
1210 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001211 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001212
1213 [formatter_form1]
1214 format=%(levelname)s ++ %(message)s
1215 datefmt=
1216
1217 [formatter_form2]
1218 format=%(message)s
1219 datefmt=
1220 """
1221
Vinay Sajip3f84b072011-03-07 17:49:33 +00001222 # config7 adds a compiler logger.
1223 config7 = """
1224 [loggers]
1225 keys=root,parser,compiler
1226
1227 [handlers]
1228 keys=hand1
1229
1230 [formatters]
1231 keys=form1
1232
1233 [logger_root]
1234 level=WARNING
1235 handlers=hand1
1236
1237 [logger_compiler]
1238 level=DEBUG
1239 handlers=
1240 propagate=1
1241 qualname=compiler
1242
1243 [logger_parser]
1244 level=DEBUG
1245 handlers=
1246 propagate=1
1247 qualname=compiler.parser
1248
1249 [handler_hand1]
1250 class=StreamHandler
1251 level=NOTSET
1252 formatter=form1
1253 args=(sys.stdout,)
1254
1255 [formatter_form1]
1256 format=%(levelname)s ++ %(message)s
1257 datefmt=
1258 """
1259
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001260 disable_test = """
1261 [loggers]
1262 keys=root
1263
1264 [handlers]
1265 keys=screen
1266
1267 [formatters]
1268 keys=
1269
1270 [logger_root]
1271 level=DEBUG
1272 handlers=screen
1273
1274 [handler_screen]
1275 level=DEBUG
1276 class=StreamHandler
1277 args=(sys.stdout,)
1278 formatter=
1279 """
1280
1281 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001282 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001283 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001284
1285 def test_config0_ok(self):
1286 # A simple config file which overrides the default settings.
1287 with captured_stdout() as output:
1288 self.apply_config(self.config0)
1289 logger = logging.getLogger()
1290 # Won't output anything
1291 logger.info(self.next_message())
1292 # Outputs a message
1293 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001294 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001295 ('ERROR', '2'),
1296 ], stream=output)
1297 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001298 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001299
Georg Brandl3dbca812008-07-23 16:10:53 +00001300 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001301 # A config file defining a sub-parser as well.
1302 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001303 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001304 logger = logging.getLogger("compiler.parser")
1305 # Both will output a message
1306 logger.info(self.next_message())
1307 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001308 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001309 ('INFO', '1'),
1310 ('ERROR', '2'),
1311 ], stream=output)
1312 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001313 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001314
1315 def test_config2_failure(self):
1316 # A simple config file which overrides the default settings.
1317 self.assertRaises(Exception, self.apply_config, self.config2)
1318
1319 def test_config3_failure(self):
1320 # A simple config file which overrides the default settings.
1321 self.assertRaises(Exception, self.apply_config, self.config3)
1322
1323 def test_config4_ok(self):
1324 # A config file specifying a custom formatter class.
1325 with captured_stdout() as output:
1326 self.apply_config(self.config4)
1327 logger = logging.getLogger()
1328 try:
1329 raise RuntimeError()
1330 except RuntimeError:
1331 logging.exception("just testing")
1332 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001333 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001334 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1335 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001336 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001337
Georg Brandl3dbca812008-07-23 16:10:53 +00001338 def test_config5_ok(self):
1339 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001340
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001341 def test_config6_ok(self):
1342 self.test_config1_ok(config=self.config6)
1343
Vinay Sajip3f84b072011-03-07 17:49:33 +00001344 def test_config7_ok(self):
1345 with captured_stdout() as output:
1346 self.apply_config(self.config1a)
1347 logger = logging.getLogger("compiler.parser")
1348 # See issue #11424. compiler-hyphenated sorts
1349 # between compiler and compiler.xyz and this
1350 # was preventing compiler.xyz from being included
1351 # in the child loggers of compiler because of an
1352 # overzealous loop termination condition.
1353 hyphenated = logging.getLogger('compiler-hyphenated')
1354 # All will output a message
1355 logger.info(self.next_message())
1356 logger.error(self.next_message())
1357 hyphenated.critical(self.next_message())
1358 self.assert_log_lines([
1359 ('INFO', '1'),
1360 ('ERROR', '2'),
1361 ('CRITICAL', '3'),
1362 ], stream=output)
1363 # Original logger output is empty.
1364 self.assert_log_lines([])
1365 with captured_stdout() as output:
1366 self.apply_config(self.config7)
1367 logger = logging.getLogger("compiler.parser")
1368 self.assertFalse(logger.disabled)
1369 # Both will output a message
1370 logger.info(self.next_message())
1371 logger.error(self.next_message())
1372 logger = logging.getLogger("compiler.lexer")
1373 # Both will output a message
1374 logger.info(self.next_message())
1375 logger.error(self.next_message())
1376 # Will not appear
1377 hyphenated.critical(self.next_message())
1378 self.assert_log_lines([
1379 ('INFO', '4'),
1380 ('ERROR', '5'),
1381 ('INFO', '6'),
1382 ('ERROR', '7'),
1383 ], stream=output)
1384 # Original logger output is empty.
1385 self.assert_log_lines([])
1386
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001387 def test_logger_disabling(self):
1388 self.apply_config(self.disable_test)
1389 logger = logging.getLogger('foo')
1390 self.assertFalse(logger.disabled)
1391 self.apply_config(self.disable_test)
1392 self.assertTrue(logger.disabled)
1393 self.apply_config(self.disable_test, disable_existing_loggers=False)
1394 self.assertFalse(logger.disabled)
1395
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001396
Victor Stinner45df8202010-04-28 22:31:17 +00001397@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001398class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001399
Christian Heimes180510d2008-03-03 19:15:45 +00001400 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001401
Christian Heimes180510d2008-03-03 19:15:45 +00001402 def setUp(self):
1403 """Set up a TCP server to receive log messages, and a SocketHandler
1404 pointing to that server's address and port."""
1405 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001406 addr = ('localhost', 0)
1407 self.server = server = TestTCPServer(addr, self.handle_socket,
1408 0.01)
1409 server.start()
1410 server.ready.wait()
1411 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1412 server.port)
1413 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001414 self.root_logger.removeHandler(self.root_logger.handlers[0])
1415 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001416 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001417
Christian Heimes180510d2008-03-03 19:15:45 +00001418 def tearDown(self):
1419 """Shutdown the TCP server."""
1420 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001421 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001422 self.root_logger.removeHandler(self.sock_hdlr)
1423 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001424 finally:
1425 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001426
Vinay Sajip7367d082011-05-02 13:17:27 +01001427 def handle_socket(self, request):
1428 conn = request.connection
1429 while True:
1430 chunk = conn.recv(4)
1431 if len(chunk) < 4:
1432 break
1433 slen = struct.unpack(">L", chunk)[0]
1434 chunk = conn.recv(slen)
1435 while len(chunk) < slen:
1436 chunk = chunk + conn.recv(slen - len(chunk))
1437 obj = pickle.loads(chunk)
1438 record = logging.makeLogRecord(obj)
1439 self.log_output += record.msg + '\n'
1440 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001441
Christian Heimes180510d2008-03-03 19:15:45 +00001442 def test_output(self):
1443 # The log message sent to the SocketHandler is properly received.
1444 logger = logging.getLogger("tcp")
1445 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001446 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001447 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001448 self.handled.acquire()
1449 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001450
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001451 def test_noserver(self):
1452 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001453 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001454 #The logging call should try to connect, which should fail
1455 try:
1456 raise RuntimeError('Deliberate mistake')
1457 except RuntimeError:
1458 self.root_logger.exception('Never sent')
1459 self.root_logger.error('Never sent, either')
1460 now = time.time()
1461 self.assertTrue(self.sock_hdlr.retryTime > now)
1462 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1463 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001464
Vinay Sajip7367d082011-05-02 13:17:27 +01001465
1466@unittest.skipUnless(threading, 'Threading required for this test.')
1467class DatagramHandlerTest(BaseTest):
1468
1469 """Test for DatagramHandler."""
1470
1471 def setUp(self):
1472 """Set up a UDP server to receive log messages, and a DatagramHandler
1473 pointing to that server's address and port."""
1474 BaseTest.setUp(self)
1475 addr = ('localhost', 0)
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001476 self.server = server = TestUDPServer(addr, self.handle_datagram, 0.01)
Vinay Sajip7367d082011-05-02 13:17:27 +01001477 server.start()
1478 server.ready.wait()
1479 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1480 server.port)
1481 self.log_output = ''
1482 self.root_logger.removeHandler(self.root_logger.handlers[0])
1483 self.root_logger.addHandler(self.sock_hdlr)
1484 self.handled = threading.Event()
1485
1486 def tearDown(self):
1487 """Shutdown the UDP server."""
1488 try:
1489 self.server.stop(2.0)
1490 self.root_logger.removeHandler(self.sock_hdlr)
1491 self.sock_hdlr.close()
1492 finally:
1493 BaseTest.tearDown(self)
1494
1495 def handle_datagram(self, request):
1496 slen = struct.pack('>L', 0) # length of prefix
1497 packet = request.packet[len(slen):]
1498 obj = pickle.loads(packet)
1499 record = logging.makeLogRecord(obj)
1500 self.log_output += record.msg + '\n'
1501 self.handled.set()
1502
1503 def test_output(self):
1504 # The log message sent to the DatagramHandler is properly received.
1505 logger = logging.getLogger("udp")
1506 logger.error("spam")
1507 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001508 self.handled.clear()
1509 logger.error("eggs")
1510 self.handled.wait()
1511 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001512
1513
1514@unittest.skipUnless(threading, 'Threading required for this test.')
1515class SysLogHandlerTest(BaseTest):
1516
1517 """Test for SysLogHandler using UDP."""
1518
1519 def setUp(self):
1520 """Set up a UDP server to receive log messages, and a SysLogHandler
1521 pointing to that server's address and port."""
1522 BaseTest.setUp(self)
1523 addr = ('localhost', 0)
1524 self.server = server = TestUDPServer(addr, self.handle_datagram,
1525 0.01)
1526 server.start()
1527 server.ready.wait()
1528 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1529 server.port))
1530 self.log_output = ''
1531 self.root_logger.removeHandler(self.root_logger.handlers[0])
1532 self.root_logger.addHandler(self.sl_hdlr)
1533 self.handled = threading.Event()
1534
1535 def tearDown(self):
1536 """Shutdown the UDP server."""
1537 try:
1538 self.server.stop(2.0)
1539 self.root_logger.removeHandler(self.sl_hdlr)
1540 self.sl_hdlr.close()
1541 finally:
1542 BaseTest.tearDown(self)
1543
1544 def handle_datagram(self, request):
1545 self.log_output = request.packet
1546 self.handled.set()
1547
1548 def test_output(self):
1549 # The log message sent to the SysLogHandler is properly received.
1550 logger = logging.getLogger("slh")
1551 logger.error("sp\xe4m")
1552 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001553 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001554 self.handled.clear()
1555 self.sl_hdlr.append_nul = False
1556 logger.error("sp\xe4m")
1557 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001558 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001559 self.handled.clear()
1560 self.sl_hdlr.ident = "h\xe4m-"
1561 logger.error("sp\xe4m")
1562 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001563 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001564
1565
1566@unittest.skipUnless(threading, 'Threading required for this test.')
1567class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001568 """Test for HTTPHandler."""
1569
Vinay Sajip9ba87612011-05-21 11:32:15 +01001570 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1571MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1572zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1573aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1574AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
15750tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
15765LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1577piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
15782z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1579i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1580PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1581E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1582rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1583/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1584-----END RSA PRIVATE KEY-----
1585-----BEGIN CERTIFICATE-----
1586MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1587BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1588EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1589xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1590l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1591vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1592BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1593S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1594BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15954f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15961c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1597IRbTpvlHWPjsSvHz0ZOH
1598-----END CERTIFICATE-----"""
1599
Vinay Sajip7367d082011-05-02 13:17:27 +01001600 def setUp(self):
1601 """Set up an HTTP server to receive log messages, and a HTTPHandler
1602 pointing to that server's address and port."""
1603 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001604 self.handled = threading.Event()
1605
Vinay Sajip7367d082011-05-02 13:17:27 +01001606 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001607 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001608 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001609 if self.command == 'POST':
1610 try:
1611 rlen = int(request.headers['Content-Length'])
1612 self.post_data = request.rfile.read(rlen)
1613 except:
1614 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001615 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001616 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001617 self.handled.set()
1618
1619 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001620 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001621 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001622 root_logger = self.root_logger
1623 root_logger.removeHandler(self.root_logger.handlers[0])
1624 for secure in (False, True):
1625 addr = ('localhost', 0)
1626 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001627 try:
1628 import ssl
1629 fd, fn = tempfile.mkstemp()
1630 os.close(fd)
1631 with open(fn, 'w') as f:
1632 f.write(self.PEMFILE)
1633 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1634 sslctx.load_cert_chain(fn)
1635 os.unlink(fn)
1636 except ImportError:
1637 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001638 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001639 sslctx = None
1640 self.server = server = TestHTTPServer(addr, self.handle_request,
1641 0.01, sslctx=sslctx)
1642 server.start()
1643 server.ready.wait()
1644 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001645 secure_client = secure and sslctx
1646 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1647 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001648 self.log_data = None
1649 root_logger.addHandler(self.h_hdlr)
1650
1651 for method in ('GET', 'POST'):
1652 self.h_hdlr.method = method
1653 self.handled.clear()
1654 msg = "sp\xe4m"
1655 logger.error(msg)
1656 self.handled.wait()
1657 self.assertEqual(self.log_data.path, '/frob')
1658 self.assertEqual(self.command, method)
1659 if method == 'GET':
1660 d = parse_qs(self.log_data.query)
1661 else:
1662 d = parse_qs(self.post_data.decode('utf-8'))
1663 self.assertEqual(d['name'], ['http'])
1664 self.assertEqual(d['funcName'], ['test_output'])
1665 self.assertEqual(d['msg'], [msg])
1666
1667 self.server.stop(2.0)
1668 self.root_logger.removeHandler(self.h_hdlr)
1669 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001670
Christian Heimes180510d2008-03-03 19:15:45 +00001671class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001672
Christian Heimes180510d2008-03-03 19:15:45 +00001673 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001674
Christian Heimes180510d2008-03-03 19:15:45 +00001675 def setUp(self):
1676 """Create a dict to remember potentially destroyed objects."""
1677 BaseTest.setUp(self)
1678 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001679
Christian Heimes180510d2008-03-03 19:15:45 +00001680 def _watch_for_survival(self, *args):
1681 """Watch the given objects for survival, by creating weakrefs to
1682 them."""
1683 for obj in args:
1684 key = id(obj), repr(obj)
1685 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001686
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001687 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001688 """Assert that all objects watched for survival have survived."""
1689 # Trigger cycle breaking.
1690 gc.collect()
1691 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001692 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001693 if ref() is None:
1694 dead.append(repr_)
1695 if dead:
1696 self.fail("%d objects should have survived "
1697 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001698
Christian Heimes180510d2008-03-03 19:15:45 +00001699 def test_persistent_loggers(self):
1700 # Logger objects are persistent and retain their configuration, even
1701 # if visible references are destroyed.
1702 self.root_logger.setLevel(logging.INFO)
1703 foo = logging.getLogger("foo")
1704 self._watch_for_survival(foo)
1705 foo.setLevel(logging.DEBUG)
1706 self.root_logger.debug(self.next_message())
1707 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001708 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001709 ('foo', 'DEBUG', '2'),
1710 ])
1711 del foo
1712 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001713 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001714 # foo has retained its settings.
1715 bar = logging.getLogger("foo")
1716 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001717 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001718 ('foo', 'DEBUG', '2'),
1719 ('foo', 'DEBUG', '3'),
1720 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001721
Benjamin Petersonf91df042009-02-13 02:50:59 +00001722
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001723class EncodingTest(BaseTest):
1724 def test_encoding_plain_file(self):
1725 # In Python 2.x, a plain file object is treated as having no encoding.
1726 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001727 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1728 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001729 # the non-ascii data we write to the log.
1730 data = "foo\x80"
1731 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001732 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001733 log.addHandler(handler)
1734 try:
1735 # write non-ascii data to the log.
1736 log.warning(data)
1737 finally:
1738 log.removeHandler(handler)
1739 handler.close()
1740 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001741 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001742 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001743 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001744 finally:
1745 f.close()
1746 finally:
1747 if os.path.isfile(fn):
1748 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001749
Benjamin Petersonf91df042009-02-13 02:50:59 +00001750 def test_encoding_cyrillic_unicode(self):
1751 log = logging.getLogger("test")
1752 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1753 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1754 #Ensure it's written in a Cyrillic encoding
1755 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001756 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001757 stream = io.BytesIO()
1758 writer = writer_class(stream, 'strict')
1759 handler = logging.StreamHandler(writer)
1760 log.addHandler(handler)
1761 try:
1762 log.warning(message)
1763 finally:
1764 log.removeHandler(handler)
1765 handler.close()
1766 # check we wrote exactly those bytes, ignoring trailing \n etc
1767 s = stream.getvalue()
1768 #Compare against what the data should be when encoded in CP-1251
1769 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1770
1771
Georg Brandlf9734072008-12-07 15:30:06 +00001772class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001773
Georg Brandlf9734072008-12-07 15:30:06 +00001774 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001775 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001776 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001777 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001778 warnings.filterwarnings("always", category=UserWarning)
1779 stream = io.StringIO()
1780 h = logging.StreamHandler(stream)
1781 logger = logging.getLogger("py.warnings")
1782 logger.addHandler(h)
1783 warnings.warn("I'm warning you...")
1784 logger.removeHandler(h)
1785 s = stream.getvalue()
1786 h.close()
1787 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001788
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001789 #See if an explicit file uses the original implementation
1790 a_file = io.StringIO()
1791 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1792 a_file, "Dummy line")
1793 s = a_file.getvalue()
1794 a_file.close()
1795 self.assertEqual(s,
1796 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1797
1798 def test_warnings_no_handlers(self):
1799 with warnings.catch_warnings():
1800 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001801 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001802
1803 # confirm our assumption: no loggers are set
1804 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001805 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001806
1807 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001808 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001809 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001810
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001811
1812def formatFunc(format, datefmt=None):
1813 return logging.Formatter(format, datefmt)
1814
1815def handlerFunc():
1816 return logging.StreamHandler()
1817
1818class CustomHandler(logging.StreamHandler):
1819 pass
1820
1821class ConfigDictTest(BaseTest):
1822
1823 """Reading logging config from a dictionary."""
1824
1825 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1826
1827 # config0 is a standard configuration.
1828 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001829 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001830 'formatters': {
1831 'form1' : {
1832 'format' : '%(levelname)s ++ %(message)s',
1833 },
1834 },
1835 'handlers' : {
1836 'hand1' : {
1837 'class' : 'logging.StreamHandler',
1838 'formatter' : 'form1',
1839 'level' : 'NOTSET',
1840 'stream' : 'ext://sys.stdout',
1841 },
1842 },
1843 'root' : {
1844 'level' : 'WARNING',
1845 'handlers' : ['hand1'],
1846 },
1847 }
1848
1849 # config1 adds a little to the standard configuration.
1850 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001851 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001852 'formatters': {
1853 'form1' : {
1854 'format' : '%(levelname)s ++ %(message)s',
1855 },
1856 },
1857 'handlers' : {
1858 'hand1' : {
1859 'class' : 'logging.StreamHandler',
1860 'formatter' : 'form1',
1861 'level' : 'NOTSET',
1862 'stream' : 'ext://sys.stdout',
1863 },
1864 },
1865 'loggers' : {
1866 'compiler.parser' : {
1867 'level' : 'DEBUG',
1868 'handlers' : ['hand1'],
1869 },
1870 },
1871 'root' : {
1872 'level' : 'WARNING',
1873 },
1874 }
1875
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001876 # config1a moves the handler to the root. Used with config8a
1877 config1a = {
1878 'version': 1,
1879 'formatters': {
1880 'form1' : {
1881 'format' : '%(levelname)s ++ %(message)s',
1882 },
1883 },
1884 'handlers' : {
1885 'hand1' : {
1886 'class' : 'logging.StreamHandler',
1887 'formatter' : 'form1',
1888 'level' : 'NOTSET',
1889 'stream' : 'ext://sys.stdout',
1890 },
1891 },
1892 'loggers' : {
1893 'compiler.parser' : {
1894 'level' : 'DEBUG',
1895 },
1896 },
1897 'root' : {
1898 'level' : 'WARNING',
1899 'handlers' : ['hand1'],
1900 },
1901 }
1902
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001903 # config2 has a subtle configuration error that should be reported
1904 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001905 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001906 'formatters': {
1907 'form1' : {
1908 'format' : '%(levelname)s ++ %(message)s',
1909 },
1910 },
1911 'handlers' : {
1912 'hand1' : {
1913 'class' : 'logging.StreamHandler',
1914 'formatter' : 'form1',
1915 'level' : 'NOTSET',
1916 'stream' : 'ext://sys.stdbout',
1917 },
1918 },
1919 'loggers' : {
1920 'compiler.parser' : {
1921 'level' : 'DEBUG',
1922 'handlers' : ['hand1'],
1923 },
1924 },
1925 'root' : {
1926 'level' : 'WARNING',
1927 },
1928 }
1929
1930 #As config1 but with a misspelt level on a handler
1931 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001932 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001933 'formatters': {
1934 'form1' : {
1935 'format' : '%(levelname)s ++ %(message)s',
1936 },
1937 },
1938 'handlers' : {
1939 'hand1' : {
1940 'class' : 'logging.StreamHandler',
1941 'formatter' : 'form1',
1942 'level' : 'NTOSET',
1943 'stream' : 'ext://sys.stdout',
1944 },
1945 },
1946 'loggers' : {
1947 'compiler.parser' : {
1948 'level' : 'DEBUG',
1949 'handlers' : ['hand1'],
1950 },
1951 },
1952 'root' : {
1953 'level' : 'WARNING',
1954 },
1955 }
1956
1957
1958 #As config1 but with a misspelt level on a logger
1959 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001960 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001961 'formatters': {
1962 'form1' : {
1963 'format' : '%(levelname)s ++ %(message)s',
1964 },
1965 },
1966 'handlers' : {
1967 'hand1' : {
1968 'class' : 'logging.StreamHandler',
1969 'formatter' : 'form1',
1970 'level' : 'NOTSET',
1971 'stream' : 'ext://sys.stdout',
1972 },
1973 },
1974 'loggers' : {
1975 'compiler.parser' : {
1976 'level' : 'DEBUG',
1977 'handlers' : ['hand1'],
1978 },
1979 },
1980 'root' : {
1981 'level' : 'WRANING',
1982 },
1983 }
1984
1985 # config3 has a less subtle configuration error
1986 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001987 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001988 'formatters': {
1989 'form1' : {
1990 'format' : '%(levelname)s ++ %(message)s',
1991 },
1992 },
1993 'handlers' : {
1994 'hand1' : {
1995 'class' : 'logging.StreamHandler',
1996 'formatter' : 'misspelled_name',
1997 'level' : 'NOTSET',
1998 'stream' : 'ext://sys.stdout',
1999 },
2000 },
2001 'loggers' : {
2002 'compiler.parser' : {
2003 'level' : 'DEBUG',
2004 'handlers' : ['hand1'],
2005 },
2006 },
2007 'root' : {
2008 'level' : 'WARNING',
2009 },
2010 }
2011
2012 # config4 specifies a custom formatter class to be loaded
2013 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002014 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002015 'formatters': {
2016 'form1' : {
2017 '()' : __name__ + '.ExceptionFormatter',
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 },
2029 'root' : {
2030 'level' : 'NOTSET',
2031 'handlers' : ['hand1'],
2032 },
2033 }
2034
2035 # As config4 but using an actual callable rather than a string
2036 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002037 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002038 'formatters': {
2039 'form1' : {
2040 '()' : ExceptionFormatter,
2041 'format' : '%(levelname)s:%(name)s:%(message)s',
2042 },
2043 'form2' : {
2044 '()' : __name__ + '.formatFunc',
2045 'format' : '%(levelname)s:%(name)s:%(message)s',
2046 },
2047 'form3' : {
2048 '()' : formatFunc,
2049 'format' : '%(levelname)s:%(name)s:%(message)s',
2050 },
2051 },
2052 'handlers' : {
2053 'hand1' : {
2054 'class' : 'logging.StreamHandler',
2055 'formatter' : 'form1',
2056 'level' : 'NOTSET',
2057 'stream' : 'ext://sys.stdout',
2058 },
2059 'hand2' : {
2060 '()' : handlerFunc,
2061 },
2062 },
2063 'root' : {
2064 'level' : 'NOTSET',
2065 'handlers' : ['hand1'],
2066 },
2067 }
2068
2069 # config5 specifies a custom handler class to be loaded
2070 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002071 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002072 'formatters': {
2073 'form1' : {
2074 'format' : '%(levelname)s ++ %(message)s',
2075 },
2076 },
2077 'handlers' : {
2078 'hand1' : {
2079 'class' : __name__ + '.CustomHandler',
2080 'formatter' : 'form1',
2081 'level' : 'NOTSET',
2082 'stream' : 'ext://sys.stdout',
2083 },
2084 },
2085 'loggers' : {
2086 'compiler.parser' : {
2087 'level' : 'DEBUG',
2088 'handlers' : ['hand1'],
2089 },
2090 },
2091 'root' : {
2092 'level' : 'WARNING',
2093 },
2094 }
2095
2096 # config6 specifies a custom handler class to be loaded
2097 # but has bad arguments
2098 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002099 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002100 'formatters': {
2101 'form1' : {
2102 'format' : '%(levelname)s ++ %(message)s',
2103 },
2104 },
2105 'handlers' : {
2106 'hand1' : {
2107 'class' : __name__ + '.CustomHandler',
2108 'formatter' : 'form1',
2109 'level' : 'NOTSET',
2110 'stream' : 'ext://sys.stdout',
2111 '9' : 'invalid parameter name',
2112 },
2113 },
2114 'loggers' : {
2115 'compiler.parser' : {
2116 'level' : 'DEBUG',
2117 'handlers' : ['hand1'],
2118 },
2119 },
2120 'root' : {
2121 'level' : 'WARNING',
2122 },
2123 }
2124
2125 #config 7 does not define compiler.parser but defines compiler.lexer
2126 #so compiler.parser should be disabled after applying it
2127 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002128 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002129 'formatters': {
2130 'form1' : {
2131 'format' : '%(levelname)s ++ %(message)s',
2132 },
2133 },
2134 'handlers' : {
2135 'hand1' : {
2136 'class' : 'logging.StreamHandler',
2137 'formatter' : 'form1',
2138 'level' : 'NOTSET',
2139 'stream' : 'ext://sys.stdout',
2140 },
2141 },
2142 'loggers' : {
2143 'compiler.lexer' : {
2144 'level' : 'DEBUG',
2145 'handlers' : ['hand1'],
2146 },
2147 },
2148 'root' : {
2149 'level' : 'WARNING',
2150 },
2151 }
2152
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002153 # config8 defines both compiler and compiler.lexer
2154 # so compiler.parser should not be disabled (since
2155 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002156 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002157 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002158 'disable_existing_loggers' : False,
2159 'formatters': {
2160 'form1' : {
2161 'format' : '%(levelname)s ++ %(message)s',
2162 },
2163 },
2164 'handlers' : {
2165 'hand1' : {
2166 'class' : 'logging.StreamHandler',
2167 'formatter' : 'form1',
2168 'level' : 'NOTSET',
2169 'stream' : 'ext://sys.stdout',
2170 },
2171 },
2172 'loggers' : {
2173 'compiler' : {
2174 'level' : 'DEBUG',
2175 'handlers' : ['hand1'],
2176 },
2177 'compiler.lexer' : {
2178 },
2179 },
2180 'root' : {
2181 'level' : 'WARNING',
2182 },
2183 }
2184
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002185 # config8a disables existing loggers
2186 config8a = {
2187 'version': 1,
2188 'disable_existing_loggers' : True,
2189 'formatters': {
2190 'form1' : {
2191 'format' : '%(levelname)s ++ %(message)s',
2192 },
2193 },
2194 'handlers' : {
2195 'hand1' : {
2196 'class' : 'logging.StreamHandler',
2197 'formatter' : 'form1',
2198 'level' : 'NOTSET',
2199 'stream' : 'ext://sys.stdout',
2200 },
2201 },
2202 'loggers' : {
2203 'compiler' : {
2204 'level' : 'DEBUG',
2205 'handlers' : ['hand1'],
2206 },
2207 'compiler.lexer' : {
2208 },
2209 },
2210 'root' : {
2211 'level' : 'WARNING',
2212 },
2213 }
2214
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002215 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002216 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002217 'formatters': {
2218 'form1' : {
2219 'format' : '%(levelname)s ++ %(message)s',
2220 },
2221 },
2222 'handlers' : {
2223 'hand1' : {
2224 'class' : 'logging.StreamHandler',
2225 'formatter' : 'form1',
2226 'level' : 'WARNING',
2227 'stream' : 'ext://sys.stdout',
2228 },
2229 },
2230 'loggers' : {
2231 'compiler.parser' : {
2232 'level' : 'WARNING',
2233 'handlers' : ['hand1'],
2234 },
2235 },
2236 'root' : {
2237 'level' : 'NOTSET',
2238 },
2239 }
2240
2241 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002242 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002243 'incremental' : True,
2244 'handlers' : {
2245 'hand1' : {
2246 'level' : 'WARNING',
2247 },
2248 },
2249 'loggers' : {
2250 'compiler.parser' : {
2251 'level' : 'INFO',
2252 },
2253 },
2254 }
2255
2256 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002257 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002258 'incremental' : True,
2259 'handlers' : {
2260 'hand1' : {
2261 'level' : 'INFO',
2262 },
2263 },
2264 'loggers' : {
2265 'compiler.parser' : {
2266 'level' : 'INFO',
2267 },
2268 },
2269 }
2270
2271 #As config1 but with a filter added
2272 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002273 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002274 'formatters': {
2275 'form1' : {
2276 'format' : '%(levelname)s ++ %(message)s',
2277 },
2278 },
2279 'filters' : {
2280 'filt1' : {
2281 'name' : 'compiler.parser',
2282 },
2283 },
2284 'handlers' : {
2285 'hand1' : {
2286 'class' : 'logging.StreamHandler',
2287 'formatter' : 'form1',
2288 'level' : 'NOTSET',
2289 'stream' : 'ext://sys.stdout',
2290 'filters' : ['filt1'],
2291 },
2292 },
2293 'loggers' : {
2294 'compiler.parser' : {
2295 'level' : 'DEBUG',
2296 'filters' : ['filt1'],
2297 },
2298 },
2299 'root' : {
2300 'level' : 'WARNING',
2301 'handlers' : ['hand1'],
2302 },
2303 }
2304
2305 #As config1 but using cfg:// references
2306 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002307 'version': 1,
2308 'true_formatters': {
2309 'form1' : {
2310 'format' : '%(levelname)s ++ %(message)s',
2311 },
2312 },
2313 'handler_configs': {
2314 'hand1' : {
2315 'class' : 'logging.StreamHandler',
2316 'formatter' : 'form1',
2317 'level' : 'NOTSET',
2318 'stream' : 'ext://sys.stdout',
2319 },
2320 },
2321 'formatters' : 'cfg://true_formatters',
2322 'handlers' : {
2323 'hand1' : 'cfg://handler_configs[hand1]',
2324 },
2325 'loggers' : {
2326 'compiler.parser' : {
2327 'level' : 'DEBUG',
2328 'handlers' : ['hand1'],
2329 },
2330 },
2331 'root' : {
2332 'level' : 'WARNING',
2333 },
2334 }
2335
2336 #As config11 but missing the version key
2337 config12 = {
2338 '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 #As config11 but using an unsupported version
2367 config13 = {
2368 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002369 'true_formatters': {
2370 'form1' : {
2371 'format' : '%(levelname)s ++ %(message)s',
2372 },
2373 },
2374 'handler_configs': {
2375 'hand1' : {
2376 'class' : 'logging.StreamHandler',
2377 'formatter' : 'form1',
2378 'level' : 'NOTSET',
2379 'stream' : 'ext://sys.stdout',
2380 },
2381 },
2382 'formatters' : 'cfg://true_formatters',
2383 'handlers' : {
2384 'hand1' : 'cfg://handler_configs[hand1]',
2385 },
2386 'loggers' : {
2387 'compiler.parser' : {
2388 'level' : 'DEBUG',
2389 'handlers' : ['hand1'],
2390 },
2391 },
2392 'root' : {
2393 'level' : 'WARNING',
2394 },
2395 }
2396
Vinay Sajip3f885b52013-03-22 15:19:54 +00002397 out_of_order = {
2398 "version": 1,
2399 "formatters": {
2400 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002401 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2402 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002403 }
2404 },
2405 "handlers": {
2406 "fileGlobal": {
2407 "class": "logging.StreamHandler",
2408 "level": "DEBUG",
2409 "formatter": "mySimpleFormatter"
2410 },
2411 "bufferGlobal": {
2412 "class": "logging.handlers.MemoryHandler",
2413 "capacity": 5,
2414 "formatter": "mySimpleFormatter",
2415 "target": "fileGlobal",
2416 "level": "DEBUG"
2417 }
2418 },
2419 "loggers": {
2420 "mymodule": {
2421 "level": "DEBUG",
2422 "handlers": ["bufferGlobal"],
2423 "propagate": "true"
2424 }
2425 }
2426 }
2427
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002428 def apply_config(self, conf):
2429 logging.config.dictConfig(conf)
2430
2431 def test_config0_ok(self):
2432 # A simple config which overrides the default settings.
2433 with captured_stdout() as output:
2434 self.apply_config(self.config0)
2435 logger = logging.getLogger()
2436 # Won't output anything
2437 logger.info(self.next_message())
2438 # Outputs a message
2439 logger.error(self.next_message())
2440 self.assert_log_lines([
2441 ('ERROR', '2'),
2442 ], stream=output)
2443 # Original logger output is empty.
2444 self.assert_log_lines([])
2445
2446 def test_config1_ok(self, config=config1):
2447 # A config defining a sub-parser as well.
2448 with captured_stdout() as output:
2449 self.apply_config(config)
2450 logger = logging.getLogger("compiler.parser")
2451 # Both will output a message
2452 logger.info(self.next_message())
2453 logger.error(self.next_message())
2454 self.assert_log_lines([
2455 ('INFO', '1'),
2456 ('ERROR', '2'),
2457 ], stream=output)
2458 # Original logger output is empty.
2459 self.assert_log_lines([])
2460
2461 def test_config2_failure(self):
2462 # A simple config which overrides the default settings.
2463 self.assertRaises(Exception, self.apply_config, self.config2)
2464
2465 def test_config2a_failure(self):
2466 # A simple config which overrides the default settings.
2467 self.assertRaises(Exception, self.apply_config, self.config2a)
2468
2469 def test_config2b_failure(self):
2470 # A simple config which overrides the default settings.
2471 self.assertRaises(Exception, self.apply_config, self.config2b)
2472
2473 def test_config3_failure(self):
2474 # A simple config which overrides the default settings.
2475 self.assertRaises(Exception, self.apply_config, self.config3)
2476
2477 def test_config4_ok(self):
2478 # A config specifying a custom formatter class.
2479 with captured_stdout() as output:
2480 self.apply_config(self.config4)
2481 #logger = logging.getLogger()
2482 try:
2483 raise RuntimeError()
2484 except RuntimeError:
2485 logging.exception("just testing")
2486 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002487 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002488 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2489 # Original logger output is empty
2490 self.assert_log_lines([])
2491
2492 def test_config4a_ok(self):
2493 # A config specifying a custom formatter class.
2494 with captured_stdout() as output:
2495 self.apply_config(self.config4a)
2496 #logger = logging.getLogger()
2497 try:
2498 raise RuntimeError()
2499 except RuntimeError:
2500 logging.exception("just testing")
2501 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002502 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002503 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2504 # Original logger output is empty
2505 self.assert_log_lines([])
2506
2507 def test_config5_ok(self):
2508 self.test_config1_ok(config=self.config5)
2509
2510 def test_config6_failure(self):
2511 self.assertRaises(Exception, self.apply_config, self.config6)
2512
2513 def test_config7_ok(self):
2514 with captured_stdout() as output:
2515 self.apply_config(self.config1)
2516 logger = logging.getLogger("compiler.parser")
2517 # Both will output a message
2518 logger.info(self.next_message())
2519 logger.error(self.next_message())
2520 self.assert_log_lines([
2521 ('INFO', '1'),
2522 ('ERROR', '2'),
2523 ], stream=output)
2524 # Original logger output is empty.
2525 self.assert_log_lines([])
2526 with captured_stdout() as output:
2527 self.apply_config(self.config7)
2528 logger = logging.getLogger("compiler.parser")
2529 self.assertTrue(logger.disabled)
2530 logger = logging.getLogger("compiler.lexer")
2531 # Both will output a message
2532 logger.info(self.next_message())
2533 logger.error(self.next_message())
2534 self.assert_log_lines([
2535 ('INFO', '3'),
2536 ('ERROR', '4'),
2537 ], stream=output)
2538 # Original logger output is empty.
2539 self.assert_log_lines([])
2540
2541 #Same as test_config_7_ok but don't disable old loggers.
2542 def test_config_8_ok(self):
2543 with captured_stdout() as output:
2544 self.apply_config(self.config1)
2545 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002546 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002547 logger.info(self.next_message())
2548 logger.error(self.next_message())
2549 self.assert_log_lines([
2550 ('INFO', '1'),
2551 ('ERROR', '2'),
2552 ], stream=output)
2553 # Original logger output is empty.
2554 self.assert_log_lines([])
2555 with captured_stdout() as output:
2556 self.apply_config(self.config8)
2557 logger = logging.getLogger("compiler.parser")
2558 self.assertFalse(logger.disabled)
2559 # Both will output a message
2560 logger.info(self.next_message())
2561 logger.error(self.next_message())
2562 logger = logging.getLogger("compiler.lexer")
2563 # Both will output a message
2564 logger.info(self.next_message())
2565 logger.error(self.next_message())
2566 self.assert_log_lines([
2567 ('INFO', '3'),
2568 ('ERROR', '4'),
2569 ('INFO', '5'),
2570 ('ERROR', '6'),
2571 ], stream=output)
2572 # Original logger output is empty.
2573 self.assert_log_lines([])
2574
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002575 def test_config_8a_ok(self):
2576 with captured_stdout() as output:
2577 self.apply_config(self.config1a)
2578 logger = logging.getLogger("compiler.parser")
2579 # See issue #11424. compiler-hyphenated sorts
2580 # between compiler and compiler.xyz and this
2581 # was preventing compiler.xyz from being included
2582 # in the child loggers of compiler because of an
2583 # overzealous loop termination condition.
2584 hyphenated = logging.getLogger('compiler-hyphenated')
2585 # All will output a message
2586 logger.info(self.next_message())
2587 logger.error(self.next_message())
2588 hyphenated.critical(self.next_message())
2589 self.assert_log_lines([
2590 ('INFO', '1'),
2591 ('ERROR', '2'),
2592 ('CRITICAL', '3'),
2593 ], stream=output)
2594 # Original logger output is empty.
2595 self.assert_log_lines([])
2596 with captured_stdout() as output:
2597 self.apply_config(self.config8a)
2598 logger = logging.getLogger("compiler.parser")
2599 self.assertFalse(logger.disabled)
2600 # Both will output a message
2601 logger.info(self.next_message())
2602 logger.error(self.next_message())
2603 logger = logging.getLogger("compiler.lexer")
2604 # Both will output a message
2605 logger.info(self.next_message())
2606 logger.error(self.next_message())
2607 # Will not appear
2608 hyphenated.critical(self.next_message())
2609 self.assert_log_lines([
2610 ('INFO', '4'),
2611 ('ERROR', '5'),
2612 ('INFO', '6'),
2613 ('ERROR', '7'),
2614 ], stream=output)
2615 # Original logger output is empty.
2616 self.assert_log_lines([])
2617
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002618 def test_config_9_ok(self):
2619 with captured_stdout() as output:
2620 self.apply_config(self.config9)
2621 logger = logging.getLogger("compiler.parser")
2622 #Nothing will be output since both handler and logger are set to WARNING
2623 logger.info(self.next_message())
2624 self.assert_log_lines([], stream=output)
2625 self.apply_config(self.config9a)
2626 #Nothing will be output since both handler is still set to WARNING
2627 logger.info(self.next_message())
2628 self.assert_log_lines([], stream=output)
2629 self.apply_config(self.config9b)
2630 #Message should now be output
2631 logger.info(self.next_message())
2632 self.assert_log_lines([
2633 ('INFO', '3'),
2634 ], stream=output)
2635
2636 def test_config_10_ok(self):
2637 with captured_stdout() as output:
2638 self.apply_config(self.config10)
2639 logger = logging.getLogger("compiler.parser")
2640 logger.warning(self.next_message())
2641 logger = logging.getLogger('compiler')
2642 #Not output, because filtered
2643 logger.warning(self.next_message())
2644 logger = logging.getLogger('compiler.lexer')
2645 #Not output, because filtered
2646 logger.warning(self.next_message())
2647 logger = logging.getLogger("compiler.parser.codegen")
2648 #Output, as not filtered
2649 logger.error(self.next_message())
2650 self.assert_log_lines([
2651 ('WARNING', '1'),
2652 ('ERROR', '4'),
2653 ], stream=output)
2654
2655 def test_config11_ok(self):
2656 self.test_config1_ok(self.config11)
2657
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002658 def test_config12_failure(self):
2659 self.assertRaises(Exception, self.apply_config, self.config12)
2660
2661 def test_config13_failure(self):
2662 self.assertRaises(Exception, self.apply_config, self.config13)
2663
Victor Stinner45df8202010-04-28 22:31:17 +00002664 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002665 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002666 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002667 # Ask for a randomly assigned port (by using port 0)
2668 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002669 t.start()
2670 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002671 # Now get the port allocated
2672 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002673 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002674 try:
2675 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2676 sock.settimeout(2.0)
2677 sock.connect(('localhost', port))
2678
2679 slen = struct.pack('>L', len(text))
2680 s = slen + text
2681 sentsofar = 0
2682 left = len(s)
2683 while left > 0:
2684 sent = sock.send(s[sentsofar:])
2685 sentsofar += sent
2686 left -= sent
2687 sock.close()
2688 finally:
2689 t.ready.wait(2.0)
2690 logging.config.stopListening()
2691 t.join(2.0)
2692
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002693 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002694 def test_listen_config_10_ok(self):
2695 with captured_stdout() as output:
2696 self.setup_via_listener(json.dumps(self.config10))
2697 logger = logging.getLogger("compiler.parser")
2698 logger.warning(self.next_message())
2699 logger = logging.getLogger('compiler')
2700 #Not output, because filtered
2701 logger.warning(self.next_message())
2702 logger = logging.getLogger('compiler.lexer')
2703 #Not output, because filtered
2704 logger.warning(self.next_message())
2705 logger = logging.getLogger("compiler.parser.codegen")
2706 #Output, as not filtered
2707 logger.error(self.next_message())
2708 self.assert_log_lines([
2709 ('WARNING', '1'),
2710 ('ERROR', '4'),
2711 ], stream=output)
2712
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002713 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002714 def test_listen_config_1_ok(self):
2715 with captured_stdout() as output:
2716 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2717 logger = logging.getLogger("compiler.parser")
2718 # Both will output a message
2719 logger.info(self.next_message())
2720 logger.error(self.next_message())
2721 self.assert_log_lines([
2722 ('INFO', '1'),
2723 ('ERROR', '2'),
2724 ], stream=output)
2725 # Original logger output is empty.
2726 self.assert_log_lines([])
2727
Vinay Sajip3f885b52013-03-22 15:19:54 +00002728 def test_out_of_order(self):
2729 self.apply_config(self.out_of_order)
2730 handler = logging.getLogger('mymodule').handlers[0]
2731 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00002732 self.assertIsInstance(handler.formatter._style,
2733 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002734
Vinay Sajip373baef2011-04-26 20:05:24 +01002735 def test_baseconfig(self):
2736 d = {
2737 'atuple': (1, 2, 3),
2738 'alist': ['a', 'b', 'c'],
2739 'adict': {'d': 'e', 'f': 3 },
2740 'nest1': ('g', ('h', 'i'), 'j'),
2741 'nest2': ['k', ['l', 'm'], 'n'],
2742 'nest3': ['o', 'cfg://alist', 'p'],
2743 }
2744 bc = logging.config.BaseConfigurator(d)
2745 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2746 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2747 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2748 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2749 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2750 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2751 v = bc.convert('cfg://nest3')
2752 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2753 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2754 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2755 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002756
2757class ManagerTest(BaseTest):
2758 def test_manager_loggerclass(self):
2759 logged = []
2760
2761 class MyLogger(logging.Logger):
2762 def _log(self, level, msg, args, exc_info=None, extra=None):
2763 logged.append(msg)
2764
2765 man = logging.Manager(None)
2766 self.assertRaises(TypeError, man.setLoggerClass, int)
2767 man.setLoggerClass(MyLogger)
2768 logger = man.getLogger('test')
2769 logger.warning('should appear in logged')
2770 logging.warning('should not appear in logged')
2771
2772 self.assertEqual(logged, ['should appear in logged'])
2773
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002774 def test_set_log_record_factory(self):
2775 man = logging.Manager(None)
2776 expected = object()
2777 man.setLogRecordFactory(expected)
2778 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002779
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002780class ChildLoggerTest(BaseTest):
2781 def test_child_loggers(self):
2782 r = logging.getLogger()
2783 l1 = logging.getLogger('abc')
2784 l2 = logging.getLogger('def.ghi')
2785 c1 = r.getChild('xyz')
2786 c2 = r.getChild('uvw.xyz')
2787 self.assertTrue(c1 is logging.getLogger('xyz'))
2788 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2789 c1 = l1.getChild('def')
2790 c2 = c1.getChild('ghi')
2791 c3 = l1.getChild('def.ghi')
2792 self.assertTrue(c1 is logging.getLogger('abc.def'))
2793 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2794 self.assertTrue(c2 is c3)
2795
2796
Vinay Sajip6fac8172010-10-19 20:44:14 +00002797class DerivedLogRecord(logging.LogRecord):
2798 pass
2799
Vinay Sajip61561522010-12-03 11:50:38 +00002800class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002801
2802 def setUp(self):
2803 class CheckingFilter(logging.Filter):
2804 def __init__(self, cls):
2805 self.cls = cls
2806
2807 def filter(self, record):
2808 t = type(record)
2809 if t is not self.cls:
2810 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2811 self.cls)
2812 raise TypeError(msg)
2813 return True
2814
2815 BaseTest.setUp(self)
2816 self.filter = CheckingFilter(DerivedLogRecord)
2817 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002818 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002819
2820 def tearDown(self):
2821 self.root_logger.removeFilter(self.filter)
2822 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002823 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002824
2825 def test_logrecord_class(self):
2826 self.assertRaises(TypeError, self.root_logger.warning,
2827 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002828 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002829 self.root_logger.error(self.next_message())
2830 self.assert_log_lines([
2831 ('root', 'ERROR', '2'),
2832 ])
2833
2834
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002835class QueueHandlerTest(BaseTest):
2836 # Do not bother with a logger name group.
2837 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2838
2839 def setUp(self):
2840 BaseTest.setUp(self)
2841 self.queue = queue.Queue(-1)
2842 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2843 self.que_logger = logging.getLogger('que')
2844 self.que_logger.propagate = False
2845 self.que_logger.setLevel(logging.WARNING)
2846 self.que_logger.addHandler(self.que_hdlr)
2847
2848 def tearDown(self):
2849 self.que_hdlr.close()
2850 BaseTest.tearDown(self)
2851
2852 def test_queue_handler(self):
2853 self.que_logger.debug(self.next_message())
2854 self.assertRaises(queue.Empty, self.queue.get_nowait)
2855 self.que_logger.info(self.next_message())
2856 self.assertRaises(queue.Empty, self.queue.get_nowait)
2857 msg = self.next_message()
2858 self.que_logger.warning(msg)
2859 data = self.queue.get_nowait()
2860 self.assertTrue(isinstance(data, logging.LogRecord))
2861 self.assertEqual(data.name, self.que_logger.name)
2862 self.assertEqual((data.msg, data.args), (msg, None))
2863
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002864 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2865 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002866 def test_queue_listener(self):
2867 handler = TestHandler(Matcher())
2868 listener = logging.handlers.QueueListener(self.queue, handler)
2869 listener.start()
2870 try:
2871 self.que_logger.warning(self.next_message())
2872 self.que_logger.error(self.next_message())
2873 self.que_logger.critical(self.next_message())
2874 finally:
2875 listener.stop()
2876 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2877 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2878 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2879
Vinay Sajip37eb3382011-04-26 20:26:41 +01002880ZERO = datetime.timedelta(0)
2881
2882class UTC(datetime.tzinfo):
2883 def utcoffset(self, dt):
2884 return ZERO
2885
2886 dst = utcoffset
2887
2888 def tzname(self, dt):
2889 return 'UTC'
2890
2891utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002892
Vinay Sajipa39c5712010-10-25 13:57:39 +00002893class FormatterTest(unittest.TestCase):
2894 def setUp(self):
2895 self.common = {
2896 'name': 'formatter.test',
2897 'level': logging.DEBUG,
2898 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2899 'lineno': 42,
2900 'exc_info': None,
2901 'func': None,
2902 'msg': 'Message with %d %s',
2903 'args': (2, 'placeholders'),
2904 }
2905 self.variants = {
2906 }
2907
2908 def get_record(self, name=None):
2909 result = dict(self.common)
2910 if name is not None:
2911 result.update(self.variants[name])
2912 return logging.makeLogRecord(result)
2913
2914 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002915 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002916 r = self.get_record()
2917 f = logging.Formatter('${%(message)s}')
2918 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2919 f = logging.Formatter('%(random)s')
2920 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002921 self.assertFalse(f.usesTime())
2922 f = logging.Formatter('%(asctime)s')
2923 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002924 f = logging.Formatter('%(asctime)-15s')
2925 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002926 f = logging.Formatter('asctime')
2927 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002928
2929 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002930 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002931 r = self.get_record()
2932 f = logging.Formatter('$%{message}%$', style='{')
2933 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2934 f = logging.Formatter('{random}', style='{')
2935 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002936 self.assertFalse(f.usesTime())
2937 f = logging.Formatter('{asctime}', style='{')
2938 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002939 f = logging.Formatter('{asctime!s:15}', style='{')
2940 self.assertTrue(f.usesTime())
2941 f = logging.Formatter('{asctime:15}', style='{')
2942 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002943 f = logging.Formatter('asctime', style='{')
2944 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002945
2946 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002947 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002948 r = self.get_record()
2949 f = logging.Formatter('$message', style='$')
2950 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2951 f = logging.Formatter('$$%${message}%$$', style='$')
2952 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2953 f = logging.Formatter('${random}', style='$')
2954 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002955 self.assertFalse(f.usesTime())
2956 f = logging.Formatter('${asctime}', style='$')
2957 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002958 f = logging.Formatter('${asctime', style='$')
2959 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002960 f = logging.Formatter('$asctime', style='$')
2961 self.assertTrue(f.usesTime())
2962 f = logging.Formatter('asctime', style='$')
2963 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002964
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002965 def test_invalid_style(self):
2966 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2967
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002968 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002969 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01002970 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
2971 # We use None to indicate we want the local timezone
2972 # We're essentially converting a UTC time to local time
2973 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002974 r.msecs = 123
2975 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002976 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002977 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2978 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002979 f.format(r)
2980 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2981
2982class TestBufferingFormatter(logging.BufferingFormatter):
2983 def formatHeader(self, records):
2984 return '[(%d)' % len(records)
2985
2986 def formatFooter(self, records):
2987 return '(%d)]' % len(records)
2988
2989class BufferingFormatterTest(unittest.TestCase):
2990 def setUp(self):
2991 self.records = [
2992 logging.makeLogRecord({'msg': 'one'}),
2993 logging.makeLogRecord({'msg': 'two'}),
2994 ]
2995
2996 def test_default(self):
2997 f = logging.BufferingFormatter()
2998 self.assertEqual('', f.format([]))
2999 self.assertEqual('onetwo', f.format(self.records))
3000
3001 def test_custom(self):
3002 f = TestBufferingFormatter()
3003 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
3004 lf = logging.Formatter('<%(message)s>')
3005 f = TestBufferingFormatter(lf)
3006 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003007
3008class ExceptionTest(BaseTest):
3009 def test_formatting(self):
3010 r = self.root_logger
3011 h = RecordingHandler()
3012 r.addHandler(h)
3013 try:
3014 raise RuntimeError('deliberate mistake')
3015 except:
3016 logging.exception('failed', stack_info=True)
3017 r.removeHandler(h)
3018 h.close()
3019 r = h.records[0]
3020 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
3021 'call last):\n'))
3022 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
3023 'deliberate mistake'))
3024 self.assertTrue(r.stack_info.startswith('Stack (most recent '
3025 'call last):\n'))
3026 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
3027 'stack_info=True)'))
3028
3029
Vinay Sajip5a27d402010-12-10 11:42:57 +00003030class LastResortTest(BaseTest):
3031 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003032 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00003033 root = self.root_logger
3034 root.removeHandler(self.root_hdlr)
3035 old_stderr = sys.stderr
3036 old_lastresort = logging.lastResort
3037 old_raise_exceptions = logging.raiseExceptions
3038 try:
3039 sys.stderr = sio = io.StringIO()
Vinay Sajip8188f582011-07-25 19:58:13 +01003040 root.debug('This should not appear')
3041 self.assertEqual(sio.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00003042 root.warning('This is your final chance!')
3043 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
3044 #No handlers and no last resort, so 'No handlers' message
3045 logging.lastResort = None
3046 sys.stderr = sio = io.StringIO()
3047 root.warning('This is your final chance!')
3048 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
3049 # 'No handlers' message only printed once
3050 sys.stderr = sio = io.StringIO()
3051 root.warning('This is your final chance!')
3052 self.assertEqual(sio.getvalue(), '')
3053 root.manager.emittedNoHandlerWarning = False
3054 #If raiseExceptions is False, no message is printed
3055 logging.raiseExceptions = False
3056 sys.stderr = sio = io.StringIO()
3057 root.warning('This is your final chance!')
3058 self.assertEqual(sio.getvalue(), '')
3059 finally:
3060 sys.stderr = old_stderr
3061 root.addHandler(self.root_hdlr)
3062 logging.lastResort = old_lastresort
3063 logging.raiseExceptions = old_raise_exceptions
3064
3065
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003066class FakeHandler:
3067
3068 def __init__(self, identifier, called):
3069 for method in ('acquire', 'flush', 'close', 'release'):
3070 setattr(self, method, self.record_call(identifier, method, called))
3071
3072 def record_call(self, identifier, method_name, called):
3073 def inner():
3074 called.append('{} - {}'.format(identifier, method_name))
3075 return inner
3076
3077
3078class RecordingHandler(logging.NullHandler):
3079
3080 def __init__(self, *args, **kwargs):
3081 super(RecordingHandler, self).__init__(*args, **kwargs)
3082 self.records = []
3083
3084 def handle(self, record):
3085 """Keep track of all the emitted records."""
3086 self.records.append(record)
3087
3088
3089class ShutdownTest(BaseTest):
3090
Vinay Sajip5e66b162011-04-20 15:41:14 +01003091 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003092
3093 def setUp(self):
3094 super(ShutdownTest, self).setUp()
3095 self.called = []
3096
3097 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003098 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003099
3100 def raise_error(self, error):
3101 def inner():
3102 raise error()
3103 return inner
3104
3105 def test_no_failure(self):
3106 # create some fake handlers
3107 handler0 = FakeHandler(0, self.called)
3108 handler1 = FakeHandler(1, self.called)
3109 handler2 = FakeHandler(2, self.called)
3110
3111 # create live weakref to those handlers
3112 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
3113
3114 logging.shutdown(handlerList=list(handlers))
3115
3116 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
3117 '1 - acquire', '1 - flush', '1 - close', '1 - release',
3118 '0 - acquire', '0 - flush', '0 - close', '0 - release']
3119 self.assertEqual(expected, self.called)
3120
3121 def _test_with_failure_in_method(self, method, error):
3122 handler = FakeHandler(0, self.called)
3123 setattr(handler, method, self.raise_error(error))
3124 handlers = [logging.weakref.ref(handler)]
3125
3126 logging.shutdown(handlerList=list(handlers))
3127
3128 self.assertEqual('0 - release', self.called[-1])
3129
3130 def test_with_ioerror_in_acquire(self):
3131 self._test_with_failure_in_method('acquire', IOError)
3132
3133 def test_with_ioerror_in_flush(self):
3134 self._test_with_failure_in_method('flush', IOError)
3135
3136 def test_with_ioerror_in_close(self):
3137 self._test_with_failure_in_method('close', IOError)
3138
3139 def test_with_valueerror_in_acquire(self):
3140 self._test_with_failure_in_method('acquire', ValueError)
3141
3142 def test_with_valueerror_in_flush(self):
3143 self._test_with_failure_in_method('flush', ValueError)
3144
3145 def test_with_valueerror_in_close(self):
3146 self._test_with_failure_in_method('close', ValueError)
3147
3148 def test_with_other_error_in_acquire_without_raise(self):
3149 logging.raiseExceptions = False
3150 self._test_with_failure_in_method('acquire', IndexError)
3151
3152 def test_with_other_error_in_flush_without_raise(self):
3153 logging.raiseExceptions = False
3154 self._test_with_failure_in_method('flush', IndexError)
3155
3156 def test_with_other_error_in_close_without_raise(self):
3157 logging.raiseExceptions = False
3158 self._test_with_failure_in_method('close', IndexError)
3159
3160 def test_with_other_error_in_acquire_with_raise(self):
3161 logging.raiseExceptions = True
3162 self.assertRaises(IndexError, self._test_with_failure_in_method,
3163 'acquire', IndexError)
3164
3165 def test_with_other_error_in_flush_with_raise(self):
3166 logging.raiseExceptions = True
3167 self.assertRaises(IndexError, self._test_with_failure_in_method,
3168 'flush', IndexError)
3169
3170 def test_with_other_error_in_close_with_raise(self):
3171 logging.raiseExceptions = True
3172 self.assertRaises(IndexError, self._test_with_failure_in_method,
3173 'close', IndexError)
3174
3175
3176class ModuleLevelMiscTest(BaseTest):
3177
Vinay Sajip5e66b162011-04-20 15:41:14 +01003178 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003179
3180 def test_disable(self):
3181 old_disable = logging.root.manager.disable
3182 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003183 self.assertEqual(old_disable, 0)
3184 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003185
3186 logging.disable(83)
3187 self.assertEqual(logging.root.manager.disable, 83)
3188
3189 def _test_log(self, method, level=None):
3190 called = []
3191 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003192 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003193
3194 recording = RecordingHandler()
3195 logging.root.addHandler(recording)
3196
3197 log_method = getattr(logging, method)
3198 if level is not None:
3199 log_method(level, "test me: %r", recording)
3200 else:
3201 log_method("test me: %r", recording)
3202
3203 self.assertEqual(len(recording.records), 1)
3204 record = recording.records[0]
3205 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3206
3207 expected_level = level if level is not None else getattr(logging, method.upper())
3208 self.assertEqual(record.levelno, expected_level)
3209
3210 # basicConfig was not called!
3211 self.assertEqual(called, [])
3212
3213 def test_log(self):
3214 self._test_log('log', logging.ERROR)
3215
3216 def test_debug(self):
3217 self._test_log('debug')
3218
3219 def test_info(self):
3220 self._test_log('info')
3221
3222 def test_warning(self):
3223 self._test_log('warning')
3224
3225 def test_error(self):
3226 self._test_log('error')
3227
3228 def test_critical(self):
3229 self._test_log('critical')
3230
3231 def test_set_logger_class(self):
3232 self.assertRaises(TypeError, logging.setLoggerClass, object)
3233
3234 class MyLogger(logging.Logger):
3235 pass
3236
3237 logging.setLoggerClass(MyLogger)
3238 self.assertEqual(logging.getLoggerClass(), MyLogger)
3239
3240 logging.setLoggerClass(logging.Logger)
3241 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3242
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003243class LogRecordTest(BaseTest):
3244 def test_str_rep(self):
3245 r = logging.makeLogRecord({})
3246 s = str(r)
3247 self.assertTrue(s.startswith('<LogRecord: '))
3248 self.assertTrue(s.endswith('>'))
3249
3250 def test_dict_arg(self):
3251 h = RecordingHandler()
3252 r = logging.getLogger()
3253 r.addHandler(h)
3254 d = {'less' : 'more' }
3255 logging.warning('less is %(less)s', d)
3256 self.assertIs(h.records[0].args, d)
3257 self.assertEqual(h.records[0].message, 'less is more')
3258 r.removeHandler(h)
3259 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003260
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003261 def test_multiprocessing(self):
3262 r = logging.makeLogRecord({})
3263 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003264 try:
3265 import multiprocessing as mp
3266 r = logging.makeLogRecord({})
3267 self.assertEqual(r.processName, mp.current_process().name)
3268 except ImportError:
3269 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003270
3271 def test_optional(self):
3272 r = logging.makeLogRecord({})
3273 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003274 if threading:
3275 NOT_NONE(r.thread)
3276 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003277 NOT_NONE(r.process)
3278 NOT_NONE(r.processName)
3279 log_threads = logging.logThreads
3280 log_processes = logging.logProcesses
3281 log_multiprocessing = logging.logMultiprocessing
3282 try:
3283 logging.logThreads = False
3284 logging.logProcesses = False
3285 logging.logMultiprocessing = False
3286 r = logging.makeLogRecord({})
3287 NONE = self.assertIsNone
3288 NONE(r.thread)
3289 NONE(r.threadName)
3290 NONE(r.process)
3291 NONE(r.processName)
3292 finally:
3293 logging.logThreads = log_threads
3294 logging.logProcesses = log_processes
3295 logging.logMultiprocessing = log_multiprocessing
3296
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003297class BasicConfigTest(unittest.TestCase):
3298
Vinay Sajip95bf5042011-04-20 11:50:56 +01003299 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003300
3301 def setUp(self):
3302 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003303 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003304 self.saved_handlers = logging._handlers.copy()
3305 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003306 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003307 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003308 logging.root.handlers = []
3309
3310 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003311 for h in logging.root.handlers[:]:
3312 logging.root.removeHandler(h)
3313 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003314 super(BasicConfigTest, self).tearDown()
3315
Vinay Sajip3def7e02011-04-20 10:58:06 +01003316 def cleanup(self):
3317 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003318 logging._handlers.clear()
3319 logging._handlers.update(self.saved_handlers)
3320 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003321 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003322
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003323 def test_no_kwargs(self):
3324 logging.basicConfig()
3325
3326 # handler defaults to a StreamHandler to sys.stderr
3327 self.assertEqual(len(logging.root.handlers), 1)
3328 handler = logging.root.handlers[0]
3329 self.assertIsInstance(handler, logging.StreamHandler)
3330 self.assertEqual(handler.stream, sys.stderr)
3331
3332 formatter = handler.formatter
3333 # format defaults to logging.BASIC_FORMAT
3334 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3335 # datefmt defaults to None
3336 self.assertIsNone(formatter.datefmt)
3337 # style defaults to %
3338 self.assertIsInstance(formatter._style, logging.PercentStyle)
3339
Florent Xiclunae41f0de2011-11-11 19:39:25 +01003340 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003341 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003342
3343 def test_filename(self):
3344 logging.basicConfig(filename='test.log')
3345
3346 self.assertEqual(len(logging.root.handlers), 1)
3347 handler = logging.root.handlers[0]
3348 self.assertIsInstance(handler, logging.FileHandler)
3349
3350 expected = logging.FileHandler('test.log', 'a')
3351 self.addCleanup(expected.close)
3352 self.assertEqual(handler.stream.mode, expected.stream.mode)
3353 self.assertEqual(handler.stream.name, expected.stream.name)
3354
3355 def test_filemode(self):
3356 logging.basicConfig(filename='test.log', filemode='wb')
3357
3358 handler = logging.root.handlers[0]
3359 expected = logging.FileHandler('test.log', 'wb')
3360 self.addCleanup(expected.close)
3361 self.assertEqual(handler.stream.mode, expected.stream.mode)
3362
3363 def test_stream(self):
3364 stream = io.StringIO()
3365 self.addCleanup(stream.close)
3366 logging.basicConfig(stream=stream)
3367
3368 self.assertEqual(len(logging.root.handlers), 1)
3369 handler = logging.root.handlers[0]
3370 self.assertIsInstance(handler, logging.StreamHandler)
3371 self.assertEqual(handler.stream, stream)
3372
3373 def test_format(self):
3374 logging.basicConfig(format='foo')
3375
3376 formatter = logging.root.handlers[0].formatter
3377 self.assertEqual(formatter._style._fmt, 'foo')
3378
3379 def test_datefmt(self):
3380 logging.basicConfig(datefmt='bar')
3381
3382 formatter = logging.root.handlers[0].formatter
3383 self.assertEqual(formatter.datefmt, 'bar')
3384
3385 def test_style(self):
3386 logging.basicConfig(style='$')
3387
3388 formatter = logging.root.handlers[0].formatter
3389 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3390
3391 def test_level(self):
3392 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003393 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003394
3395 logging.basicConfig(level=57)
3396 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003397 # Test that second call has no effect
3398 logging.basicConfig(level=58)
3399 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003400
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003401 def test_incompatible(self):
3402 assertRaises = self.assertRaises
3403 handlers = [logging.StreamHandler()]
3404 stream = sys.stderr
3405 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3406 stream=stream)
3407 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3408 handlers=handlers)
3409 assertRaises(ValueError, logging.basicConfig, stream=stream,
3410 handlers=handlers)
3411
3412 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003413 handlers = [
3414 logging.StreamHandler(),
3415 logging.StreamHandler(sys.stdout),
3416 logging.StreamHandler(),
3417 ]
3418 f = logging.Formatter()
3419 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003420 logging.basicConfig(handlers=handlers)
3421 self.assertIs(handlers[0], logging.root.handlers[0])
3422 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003423 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003424 self.assertIsNotNone(handlers[0].formatter)
3425 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003426 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003427 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3428
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003429 def _test_log(self, method, level=None):
3430 # logging.root has no handlers so basicConfig should be called
3431 called = []
3432
3433 old_basic_config = logging.basicConfig
3434 def my_basic_config(*a, **kw):
3435 old_basic_config()
3436 old_level = logging.root.level
3437 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003438 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003439 called.append((a, kw))
3440
3441 patch(self, logging, 'basicConfig', my_basic_config)
3442
3443 log_method = getattr(logging, method)
3444 if level is not None:
3445 log_method(level, "test me")
3446 else:
3447 log_method("test me")
3448
3449 # basicConfig was called with no arguments
3450 self.assertEqual(called, [((), {})])
3451
3452 def test_log(self):
3453 self._test_log('log', logging.WARNING)
3454
3455 def test_debug(self):
3456 self._test_log('debug')
3457
3458 def test_info(self):
3459 self._test_log('info')
3460
3461 def test_warning(self):
3462 self._test_log('warning')
3463
3464 def test_error(self):
3465 self._test_log('error')
3466
3467 def test_critical(self):
3468 self._test_log('critical')
3469
3470
3471class LoggerAdapterTest(unittest.TestCase):
3472
3473 def setUp(self):
3474 super(LoggerAdapterTest, self).setUp()
3475 old_handler_list = logging._handlerList[:]
3476
3477 self.recording = RecordingHandler()
3478 self.logger = logging.root
3479 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003480 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003481 self.addCleanup(self.recording.close)
3482
3483 def cleanup():
3484 logging._handlerList[:] = old_handler_list
3485
3486 self.addCleanup(cleanup)
3487 self.addCleanup(logging.shutdown)
3488 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3489
3490 def test_exception(self):
3491 msg = 'testing exception: %r'
3492 exc = None
3493 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003494 1 / 0
3495 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003496 exc = e
3497 self.adapter.exception(msg, self.recording)
3498
3499 self.assertEqual(len(self.recording.records), 1)
3500 record = self.recording.records[0]
3501 self.assertEqual(record.levelno, logging.ERROR)
3502 self.assertEqual(record.msg, msg)
3503 self.assertEqual(record.args, (self.recording,))
3504 self.assertEqual(record.exc_info,
3505 (exc.__class__, exc, exc.__traceback__))
3506
3507 def test_critical(self):
3508 msg = 'critical test! %r'
3509 self.adapter.critical(msg, self.recording)
3510
3511 self.assertEqual(len(self.recording.records), 1)
3512 record = self.recording.records[0]
3513 self.assertEqual(record.levelno, logging.CRITICAL)
3514 self.assertEqual(record.msg, msg)
3515 self.assertEqual(record.args, (self.recording,))
3516
3517 def test_is_enabled_for(self):
3518 old_disable = self.adapter.logger.manager.disable
3519 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003520 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3521 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003522 self.assertFalse(self.adapter.isEnabledFor(32))
3523
3524 def test_has_handlers(self):
3525 self.assertTrue(self.adapter.hasHandlers())
3526
3527 for handler in self.logger.handlers:
3528 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003529
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003530 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003531 self.assertFalse(self.adapter.hasHandlers())
3532
3533
3534class LoggerTest(BaseTest):
3535
3536 def setUp(self):
3537 super(LoggerTest, self).setUp()
3538 self.recording = RecordingHandler()
3539 self.logger = logging.Logger(name='blah')
3540 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003541 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003542 self.addCleanup(self.recording.close)
3543 self.addCleanup(logging.shutdown)
3544
3545 def test_set_invalid_level(self):
3546 self.assertRaises(TypeError, self.logger.setLevel, object())
3547
3548 def test_exception(self):
3549 msg = 'testing exception: %r'
3550 exc = None
3551 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003552 1 / 0
3553 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003554 exc = e
3555 self.logger.exception(msg, self.recording)
3556
3557 self.assertEqual(len(self.recording.records), 1)
3558 record = self.recording.records[0]
3559 self.assertEqual(record.levelno, logging.ERROR)
3560 self.assertEqual(record.msg, msg)
3561 self.assertEqual(record.args, (self.recording,))
3562 self.assertEqual(record.exc_info,
3563 (exc.__class__, exc, exc.__traceback__))
3564
3565 def test_log_invalid_level_with_raise(self):
3566 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003567 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003568
3569 logging.raiseExceptions = True
3570 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3571
3572 def test_log_invalid_level_no_raise(self):
3573 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003574 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003575
3576 logging.raiseExceptions = False
3577 self.logger.log('10', 'test message') # no exception happens
3578
3579 def test_find_caller_with_stack_info(self):
3580 called = []
3581 patch(self, logging.traceback, 'print_stack',
3582 lambda f, file: called.append(file.getvalue()))
3583
3584 self.logger.findCaller(stack_info=True)
3585
3586 self.assertEqual(len(called), 1)
3587 self.assertEqual('Stack (most recent call last):\n', called[0])
3588
3589 def test_make_record_with_extra_overwrite(self):
3590 name = 'my record'
3591 level = 13
3592 fn = lno = msg = args = exc_info = func = sinfo = None
3593 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3594 exc_info, func, sinfo)
3595
3596 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3597 extra = {key: 'some value'}
3598 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3599 fn, lno, msg, args, exc_info,
3600 extra=extra, sinfo=sinfo)
3601
3602 def test_make_record_with_extra_no_overwrite(self):
3603 name = 'my record'
3604 level = 13
3605 fn = lno = msg = args = exc_info = func = sinfo = None
3606 extra = {'valid_key': 'some value'}
3607 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3608 exc_info, extra=extra, sinfo=sinfo)
3609 self.assertIn('valid_key', result.__dict__)
3610
3611 def test_has_handlers(self):
3612 self.assertTrue(self.logger.hasHandlers())
3613
3614 for handler in self.logger.handlers:
3615 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003616 self.assertFalse(self.logger.hasHandlers())
3617
3618 def test_has_handlers_no_propagate(self):
3619 child_logger = logging.getLogger('blah.child')
3620 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003621 self.assertFalse(child_logger.hasHandlers())
3622
3623 def test_is_enabled_for(self):
3624 old_disable = self.logger.manager.disable
3625 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003626 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003627 self.assertFalse(self.logger.isEnabledFor(22))
3628
Florent Xicluna5252f9f2011-11-07 19:43:05 +01003629 def test_root_logger_aliases(self):
3630 root = logging.getLogger()
3631 self.assertIs(root, logging.root)
3632 self.assertIs(root, logging.getLogger(None))
3633 self.assertIs(root, logging.getLogger(''))
3634 self.assertIs(root, logging.getLogger('foo').root)
3635 self.assertIs(root, logging.getLogger('foo.bar').root)
3636 self.assertIs(root, logging.getLogger('foo').parent)
3637
3638 self.assertIsNot(root, logging.getLogger('\0'))
3639 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
3640
3641 def test_invalid_names(self):
3642 self.assertRaises(TypeError, logging.getLogger, any)
3643 self.assertRaises(TypeError, logging.getLogger, b'foo')
3644
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003645
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003646class BaseFileTest(BaseTest):
3647 "Base class for handler tests that write log files"
3648
3649 def setUp(self):
3650 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003651 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3652 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003653 self.rmfiles = []
3654
3655 def tearDown(self):
3656 for fn in self.rmfiles:
3657 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003658 if os.path.exists(self.fn):
3659 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003660 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003661
3662 def assertLogFile(self, filename):
3663 "Assert a log file is there and register it for deletion"
3664 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003665 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003666 self.rmfiles.append(filename)
3667
3668
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003669class FileHandlerTest(BaseFileTest):
3670 def test_delay(self):
3671 os.unlink(self.fn)
3672 fh = logging.FileHandler(self.fn, delay=True)
3673 self.assertIsNone(fh.stream)
3674 self.assertFalse(os.path.exists(self.fn))
3675 fh.handle(logging.makeLogRecord({}))
3676 self.assertIsNotNone(fh.stream)
3677 self.assertTrue(os.path.exists(self.fn))
3678 fh.close()
3679
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003680class RotatingFileHandlerTest(BaseFileTest):
3681 def next_rec(self):
3682 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3683 self.next_message(), None, None, None)
3684
3685 def test_should_not_rollover(self):
3686 # If maxbytes is zero rollover never occurs
3687 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3688 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003689 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003690
3691 def test_should_rollover(self):
3692 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3693 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003694 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003695
3696 def test_file_created(self):
3697 # checks that the file is created and assumes it was created
3698 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003699 rh = logging.handlers.RotatingFileHandler(self.fn)
3700 rh.emit(self.next_rec())
3701 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003702 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003703
3704 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00003705 def namer(name):
3706 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003707 rh = logging.handlers.RotatingFileHandler(
3708 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003709 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003710 rh.emit(self.next_rec())
3711 self.assertLogFile(self.fn)
3712 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003713 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003714 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003715 self.assertLogFile(namer(self.fn + ".2"))
3716 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
3717 rh.close()
3718
Eric V. Smith851cad72012-03-11 22:46:04 -07003719 @requires_zlib
Vinay Sajip23b94d02012-01-04 12:02:26 +00003720 def test_rotator(self):
3721 def namer(name):
3722 return name + ".gz"
3723
3724 def rotator(source, dest):
3725 with open(source, "rb") as sf:
3726 data = sf.read()
3727 compressed = zlib.compress(data, 9)
3728 with open(dest, "wb") as df:
3729 df.write(compressed)
3730 os.remove(source)
3731
3732 rh = logging.handlers.RotatingFileHandler(
3733 self.fn, backupCount=2, maxBytes=1)
3734 rh.rotator = rotator
3735 rh.namer = namer
3736 m1 = self.next_rec()
3737 rh.emit(m1)
3738 self.assertLogFile(self.fn)
3739 m2 = self.next_rec()
3740 rh.emit(m2)
3741 fn = namer(self.fn + ".1")
3742 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003743 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00003744 with open(fn, "rb") as f:
3745 compressed = f.read()
3746 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003747 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003748 rh.emit(self.next_rec())
3749 fn = namer(self.fn + ".2")
3750 self.assertLogFile(fn)
3751 with open(fn, "rb") as f:
3752 compressed = f.read()
3753 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003754 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003755 rh.emit(self.next_rec())
3756 fn = namer(self.fn + ".2")
3757 with open(fn, "rb") as f:
3758 compressed = f.read()
3759 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003760 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003761 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00003762 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003763
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003764class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003765 # other test methods added below
3766 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003767 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3768 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00003769 fmt = logging.Formatter('%(asctime)s %(message)s')
3770 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003771 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003772 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003773 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003774 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00003775 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003776 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01003777 fh.close()
3778 # At this point, we should have a recent rotated file which we
3779 # can test for the existence of. However, in practice, on some
3780 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003781 # in time to go to look for the log file. So, we go back a fair
3782 # bit, and stop as soon as we see a rotated file. In theory this
3783 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003784 found = False
3785 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003786 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003787 for secs in range(GO_BACK):
3788 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003789 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3790 found = os.path.exists(fn)
3791 if found:
3792 self.rmfiles.append(fn)
3793 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003794 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3795 if not found:
3796 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003797 dn, fn = os.path.split(self.fn)
3798 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003799 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3800 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00003801 for f in files:
3802 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00003803 path = os.path.join(dn, f)
3804 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00003805 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01003806 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003807
Vinay Sajip0372e102011-05-05 12:59:14 +01003808 def test_invalid(self):
3809 assertRaises = self.assertRaises
3810 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003811 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003812 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003813 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003814 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003815 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003816
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003817def secs(**kw):
3818 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3819
3820for when, exp in (('S', 1),
3821 ('M', 60),
3822 ('H', 60 * 60),
3823 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003824 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003825 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003826 ('W0', secs(days=4, hours=24)),
3827 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003828 def test_compute_rollover(self, when=when, exp=exp):
3829 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003830 self.fn, when=when, interval=1, backupCount=0, utc=True)
3831 currentTime = 0.0
3832 actual = rh.computeRollover(currentTime)
3833 if exp != actual:
3834 # Failures occur on some systems for MIDNIGHT and W0.
3835 # Print detailed calculation for MIDNIGHT so we can try to see
3836 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00003837 if when == 'MIDNIGHT':
3838 try:
3839 if rh.utc:
3840 t = time.gmtime(currentTime)
3841 else:
3842 t = time.localtime(currentTime)
3843 currentHour = t[3]
3844 currentMinute = t[4]
3845 currentSecond = t[5]
3846 # r is the number of seconds left between now and midnight
3847 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3848 currentMinute) * 60 +
3849 currentSecond)
3850 result = currentTime + r
3851 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3852 print('currentHour: %s' % currentHour, file=sys.stderr)
3853 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3854 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3855 print('r: %s' % r, file=sys.stderr)
3856 print('result: %s' % result, file=sys.stderr)
3857 except Exception:
3858 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3859 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003860 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003861 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3862
Vinay Sajip60ccd822011-05-09 17:32:09 +01003863
3864@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3865class NTEventLogHandlerTest(BaseTest):
3866 def test_basic(self):
3867 logtype = 'Application'
3868 elh = win32evtlog.OpenEventLog(None, logtype)
3869 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3870 h = logging.handlers.NTEventLogHandler('test_logging')
3871 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3872 h.handle(r)
3873 h.close()
3874 # Now see if the event is recorded
3875 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3876 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3877 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3878 found = False
3879 GO_BACK = 100
3880 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3881 for e in events:
3882 if e.SourceName != 'test_logging':
3883 continue
3884 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3885 if msg != 'Test Log Message\r\n':
3886 continue
3887 found = True
3888 break
3889 msg = 'Record not found in event log, went back %d records' % GO_BACK
3890 self.assertTrue(found, msg=msg)
3891
Christian Heimes180510d2008-03-03 19:15:45 +00003892# Set the locale to the platform-dependent default. I have no idea
3893# why the test does this, but in any case we save the current locale
3894# first and restore it at the end.
3895@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003896def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003897 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003898 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003899 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3900 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3901 ManagerTest, FormatterTest, BufferingFormatterTest,
3902 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3903 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3904 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3905 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003906 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003907 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003908 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003909 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003910
Christian Heimes180510d2008-03-03 19:15:45 +00003911if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003912 test_main()