blob: 8c6e6b81167c15b78e86e77bb00b8c0e7d3de33a [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)
Serhiy Storchaka50254c52013-08-29 11:35:43 +0300159 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100160 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000161 for actual, expected in zip(actual_lines, expected_values):
162 match = pat.search(actual)
163 if not match:
164 self.fail("Log line does not match expected pattern:\n" +
165 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000166 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000167 s = stream.read()
168 if s:
169 self.fail("Remaining output at end of log stream:\n" + s)
170
171 def next_message(self):
172 """Generate a message consisting solely of an auto-incrementing
173 integer."""
174 self.message_num += 1
175 return "%d" % self.message_num
176
177
178class BuiltinLevelsTest(BaseTest):
179 """Test builtin levels and their inheritance."""
180
181 def test_flat(self):
182 #Logging levels in a flat logger namespace.
183 m = self.next_message
184
185 ERR = logging.getLogger("ERR")
186 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000187 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000188 INF.setLevel(logging.INFO)
189 DEB = logging.getLogger("DEB")
190 DEB.setLevel(logging.DEBUG)
191
192 # These should log.
193 ERR.log(logging.CRITICAL, m())
194 ERR.error(m())
195
196 INF.log(logging.CRITICAL, m())
197 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100198 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000199 INF.info(m())
200
201 DEB.log(logging.CRITICAL, m())
202 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100203 DEB.warning(m())
204 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000205 DEB.debug(m())
206
207 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100208 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000209 ERR.info(m())
210 ERR.debug(m())
211
212 INF.debug(m())
213
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000214 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000215 ('ERR', 'CRITICAL', '1'),
216 ('ERR', 'ERROR', '2'),
217 ('INF', 'CRITICAL', '3'),
218 ('INF', 'ERROR', '4'),
219 ('INF', 'WARNING', '5'),
220 ('INF', 'INFO', '6'),
221 ('DEB', 'CRITICAL', '7'),
222 ('DEB', 'ERROR', '8'),
223 ('DEB', 'WARNING', '9'),
224 ('DEB', 'INFO', '10'),
225 ('DEB', 'DEBUG', '11'),
226 ])
227
228 def test_nested_explicit(self):
229 # Logging levels in a nested namespace, all explicitly set.
230 m = self.next_message
231
232 INF = logging.getLogger("INF")
233 INF.setLevel(logging.INFO)
234 INF_ERR = logging.getLogger("INF.ERR")
235 INF_ERR.setLevel(logging.ERROR)
236
237 # These should log.
238 INF_ERR.log(logging.CRITICAL, m())
239 INF_ERR.error(m())
240
241 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100242 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000243 INF_ERR.info(m())
244 INF_ERR.debug(m())
245
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000246 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000247 ('INF.ERR', 'CRITICAL', '1'),
248 ('INF.ERR', 'ERROR', '2'),
249 ])
250
251 def test_nested_inherited(self):
252 #Logging levels in a nested namespace, inherited from parent loggers.
253 m = self.next_message
254
255 INF = logging.getLogger("INF")
256 INF.setLevel(logging.INFO)
257 INF_ERR = logging.getLogger("INF.ERR")
258 INF_ERR.setLevel(logging.ERROR)
259 INF_UNDEF = logging.getLogger("INF.UNDEF")
260 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
261 UNDEF = logging.getLogger("UNDEF")
262
263 # These should log.
264 INF_UNDEF.log(logging.CRITICAL, m())
265 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100266 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000267 INF_UNDEF.info(m())
268 INF_ERR_UNDEF.log(logging.CRITICAL, m())
269 INF_ERR_UNDEF.error(m())
270
271 # These should not log.
272 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100273 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000274 INF_ERR_UNDEF.info(m())
275 INF_ERR_UNDEF.debug(m())
276
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000277 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000278 ('INF.UNDEF', 'CRITICAL', '1'),
279 ('INF.UNDEF', 'ERROR', '2'),
280 ('INF.UNDEF', 'WARNING', '3'),
281 ('INF.UNDEF', 'INFO', '4'),
282 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
283 ('INF.ERR.UNDEF', 'ERROR', '6'),
284 ])
285
286 def test_nested_with_virtual_parent(self):
287 # Logging levels when some parent does not exist yet.
288 m = self.next_message
289
290 INF = logging.getLogger("INF")
291 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
292 CHILD = logging.getLogger("INF.BADPARENT")
293 INF.setLevel(logging.INFO)
294
295 # These should log.
296 GRANDCHILD.log(logging.FATAL, m())
297 GRANDCHILD.info(m())
298 CHILD.log(logging.FATAL, m())
299 CHILD.info(m())
300
301 # These should not log.
302 GRANDCHILD.debug(m())
303 CHILD.debug(m())
304
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000305 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000306 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
307 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
308 ('INF.BADPARENT', 'CRITICAL', '3'),
309 ('INF.BADPARENT', 'INFO', '4'),
310 ])
311
312
313class BasicFilterTest(BaseTest):
314
315 """Test the bundled Filter class."""
316
317 def test_filter(self):
318 # Only messages satisfying the specified criteria pass through the
319 # filter.
320 filter_ = logging.Filter("spam.eggs")
321 handler = self.root_logger.handlers[0]
322 try:
323 handler.addFilter(filter_)
324 spam = logging.getLogger("spam")
325 spam_eggs = logging.getLogger("spam.eggs")
326 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
327 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
328
329 spam.info(self.next_message())
330 spam_eggs.info(self.next_message()) # Good.
331 spam_eggs_fish.info(self.next_message()) # Good.
332 spam_bakedbeans.info(self.next_message())
333
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000334 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000335 ('spam.eggs', 'INFO', '2'),
336 ('spam.eggs.fish', 'INFO', '3'),
337 ])
338 finally:
339 handler.removeFilter(filter_)
340
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000341 def test_callable_filter(self):
342 # Only messages satisfying the specified criteria pass through the
343 # filter.
344
345 def filterfunc(record):
346 parts = record.name.split('.')
347 prefix = '.'.join(parts[:2])
348 return prefix == 'spam.eggs'
349
350 handler = self.root_logger.handlers[0]
351 try:
352 handler.addFilter(filterfunc)
353 spam = logging.getLogger("spam")
354 spam_eggs = logging.getLogger("spam.eggs")
355 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
356 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
357
358 spam.info(self.next_message())
359 spam_eggs.info(self.next_message()) # Good.
360 spam_eggs_fish.info(self.next_message()) # Good.
361 spam_bakedbeans.info(self.next_message())
362
363 self.assert_log_lines([
364 ('spam.eggs', 'INFO', '2'),
365 ('spam.eggs.fish', 'INFO', '3'),
366 ])
367 finally:
368 handler.removeFilter(filterfunc)
369
Vinay Sajip985ef872011-04-26 19:34:04 +0100370 def test_empty_filter(self):
371 f = logging.Filter()
372 r = logging.makeLogRecord({'name': 'spam.eggs'})
373 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000374
375#
376# First, we define our levels. There can be as many as you want - the only
377# limitations are that they should be integers, the lowest should be > 0 and
378# larger values mean less information being logged. If you need specific
379# level values which do not fit into these limitations, you can use a
380# mapping dictionary to convert between your application levels and the
381# logging system.
382#
383SILENT = 120
384TACITURN = 119
385TERSE = 118
386EFFUSIVE = 117
387SOCIABLE = 116
388VERBOSE = 115
389TALKATIVE = 114
390GARRULOUS = 113
391CHATTERBOX = 112
392BORING = 111
393
394LEVEL_RANGE = range(BORING, SILENT + 1)
395
396#
397# Next, we define names for our levels. You don't need to do this - in which
398# case the system will use "Level n" to denote the text for the level.
399#
400my_logging_levels = {
401 SILENT : 'Silent',
402 TACITURN : 'Taciturn',
403 TERSE : 'Terse',
404 EFFUSIVE : 'Effusive',
405 SOCIABLE : 'Sociable',
406 VERBOSE : 'Verbose',
407 TALKATIVE : 'Talkative',
408 GARRULOUS : 'Garrulous',
409 CHATTERBOX : 'Chatterbox',
410 BORING : 'Boring',
411}
412
413class GarrulousFilter(logging.Filter):
414
415 """A filter which blocks garrulous messages."""
416
417 def filter(self, record):
418 return record.levelno != GARRULOUS
419
420class VerySpecificFilter(logging.Filter):
421
422 """A filter which blocks sociable and taciturn messages."""
423
424 def filter(self, record):
425 return record.levelno not in [SOCIABLE, TACITURN]
426
427
428class CustomLevelsAndFiltersTest(BaseTest):
429
430 """Test various filtering possibilities with custom logging levels."""
431
432 # Skip the logger name group.
433 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
434
435 def setUp(self):
436 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000437 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000438 logging.addLevelName(k, v)
439
440 def log_at_all_levels(self, logger):
441 for lvl in LEVEL_RANGE:
442 logger.log(lvl, self.next_message())
443
444 def test_logger_filter(self):
445 # Filter at logger level.
446 self.root_logger.setLevel(VERBOSE)
447 # Levels >= 'Verbose' are good.
448 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000449 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000450 ('Verbose', '5'),
451 ('Sociable', '6'),
452 ('Effusive', '7'),
453 ('Terse', '8'),
454 ('Taciturn', '9'),
455 ('Silent', '10'),
456 ])
457
458 def test_handler_filter(self):
459 # Filter at handler level.
460 self.root_logger.handlers[0].setLevel(SOCIABLE)
461 try:
462 # Levels >= 'Sociable' are good.
463 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000464 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000465 ('Sociable', '6'),
466 ('Effusive', '7'),
467 ('Terse', '8'),
468 ('Taciturn', '9'),
469 ('Silent', '10'),
470 ])
471 finally:
472 self.root_logger.handlers[0].setLevel(logging.NOTSET)
473
474 def test_specific_filters(self):
475 # Set a specific filter object on the handler, and then add another
476 # filter object on the logger itself.
477 handler = self.root_logger.handlers[0]
478 specific_filter = None
479 garr = GarrulousFilter()
480 handler.addFilter(garr)
481 try:
482 self.log_at_all_levels(self.root_logger)
483 first_lines = [
484 # Notice how 'Garrulous' is missing
485 ('Boring', '1'),
486 ('Chatterbox', '2'),
487 ('Talkative', '4'),
488 ('Verbose', '5'),
489 ('Sociable', '6'),
490 ('Effusive', '7'),
491 ('Terse', '8'),
492 ('Taciturn', '9'),
493 ('Silent', '10'),
494 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000495 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000496
497 specific_filter = VerySpecificFilter()
498 self.root_logger.addFilter(specific_filter)
499 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000500 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000501 # Not only 'Garrulous' is still missing, but also 'Sociable'
502 # and 'Taciturn'
503 ('Boring', '11'),
504 ('Chatterbox', '12'),
505 ('Talkative', '14'),
506 ('Verbose', '15'),
507 ('Effusive', '17'),
508 ('Terse', '18'),
509 ('Silent', '20'),
510 ])
511 finally:
512 if specific_filter:
513 self.root_logger.removeFilter(specific_filter)
514 handler.removeFilter(garr)
515
516
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100517class HandlerTest(BaseTest):
518 def test_name(self):
519 h = logging.Handler()
520 h.name = 'generic'
521 self.assertEqual(h.name, 'generic')
522 h.name = 'anothergeneric'
523 self.assertEqual(h.name, 'anothergeneric')
524 self.assertRaises(NotImplementedError, h.emit, None)
525
Vinay Sajip5a35b062011-04-27 11:31:14 +0100526 def test_builtin_handlers(self):
527 # We can't actually *use* too many handlers in the tests,
528 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200529 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100530 for existing in (True, False):
531 fd, fn = tempfile.mkstemp()
532 os.close(fd)
533 if not existing:
534 os.unlink(fn)
535 h = logging.handlers.WatchedFileHandler(fn, delay=True)
536 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100537 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100538 self.assertEqual(dev, -1)
539 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100540 r = logging.makeLogRecord({'msg': 'Test'})
541 h.handle(r)
542 # Now remove the file.
543 os.unlink(fn)
544 self.assertFalse(os.path.exists(fn))
545 # The next call should recreate the file.
546 h.handle(r)
547 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100548 else:
549 self.assertEqual(h.dev, -1)
550 self.assertEqual(h.ino, -1)
551 h.close()
552 if existing:
553 os.unlink(fn)
554 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100555 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100556 else:
557 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100558 try:
559 h = logging.handlers.SysLogHandler(sockname)
560 self.assertEqual(h.facility, h.LOG_USER)
561 self.assertTrue(h.unixsocket)
562 h.close()
563 except socket.error: # syslogd might not be available
564 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100565 for method in ('GET', 'POST', 'PUT'):
566 if method == 'PUT':
567 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
568 'localhost', '/log', method)
569 else:
570 h = logging.handlers.HTTPHandler('localhost', '/log', method)
571 h.close()
572 h = logging.handlers.BufferingHandler(0)
573 r = logging.makeLogRecord({})
574 self.assertTrue(h.shouldFlush(r))
575 h.close()
576 h = logging.handlers.BufferingHandler(1)
577 self.assertFalse(h.shouldFlush(r))
578 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100579
Vinay Sajip5e86eed2012-05-29 22:48:10 +0100580 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100581 @unittest.skipUnless(threading, 'Threading required for this test.')
582 def test_race(self):
583 # Issue #14632 refers.
584 def remove_loop(fname, tries):
585 for _ in range(tries):
586 try:
587 os.unlink(fname)
588 except OSError:
589 pass
590 time.sleep(0.004 * random.randint(0, 4))
591
Vinay Sajipc94871a2012-04-25 10:51:35 +0100592 del_count = 500
593 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100594
Vinay Sajipa5798de2012-04-24 23:33:33 +0100595 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100596 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
597 os.close(fd)
598 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
599 remover.daemon = True
600 remover.start()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100601 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100602 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
603 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100604 try:
605 for _ in range(log_count):
606 time.sleep(0.005)
607 r = logging.makeLogRecord({'msg': 'testing' })
608 h.handle(r)
609 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100610 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100611 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100612 if os.path.exists(fn):
613 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100614
615
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100616class BadStream(object):
617 def write(self, data):
618 raise RuntimeError('deliberate mistake')
619
620class TestStreamHandler(logging.StreamHandler):
621 def handleError(self, record):
622 self.error_record = record
623
624class StreamHandlerTest(BaseTest):
625 def test_error_handling(self):
626 h = TestStreamHandler(BadStream())
627 r = logging.makeLogRecord({})
628 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100629 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100630 try:
631 h.handle(r)
632 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100633 h = logging.StreamHandler(BadStream())
634 sys.stderr = sio = io.StringIO()
635 h.handle(r)
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100636 self.assertIn('\nRuntimeError: deliberate mistake\n',
637 sio.getvalue())
Vinay Sajip985ef872011-04-26 19:34:04 +0100638 logging.raiseExceptions = False
639 sys.stderr = sio = io.StringIO()
640 h.handle(r)
641 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100642 finally:
643 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100644 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100645
Vinay Sajip7367d082011-05-02 13:17:27 +0100646# -- The following section could be moved into a server_helper.py module
647# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100648
Vinay Sajipce7c9782011-05-17 07:15:53 +0100649if threading:
650 class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100651 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100652 This derived class has had to be created because smtpd does not
653 support use of custom channel maps, although they are allowed by
654 asyncore's design. Issue #11959 has been raised to address this,
655 and if resolved satisfactorily, some of this code can be removed.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100656 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100657 def __init__(self, server, conn, addr, sockmap):
658 asynchat.async_chat.__init__(self, conn, sockmap)
659 self.smtp_server = server
660 self.conn = conn
661 self.addr = addr
R David Murrayd1a30c92012-05-26 14:33:59 -0400662 self.data_size_limit = None
Vinay Sajipce7c9782011-05-17 07:15:53 +0100663 self.received_lines = []
664 self.smtp_state = self.COMMAND
665 self.seen_greeting = ''
666 self.mailfrom = None
667 self.rcpttos = []
668 self.received_data = ''
669 self.fqdn = socket.getfqdn()
670 self.num_bytes = 0
671 try:
672 self.peer = conn.getpeername()
673 except socket.error as err:
674 # a race condition may occur if the other end is closing
675 # before we can get the peername
676 self.close()
677 if err.args[0] != errno.ENOTCONN:
678 raise
679 return
680 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
681 self.set_terminator(b'\r\n')
R David Murrayd1a30c92012-05-26 14:33:59 -0400682 self.extended_smtp = False
Vinay Sajipa463d252011-04-30 21:52:48 +0100683
Vinay Sajip314b92b2011-05-02 14:31:16 +0100684
Vinay Sajipce7c9782011-05-17 07:15:53 +0100685 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100686 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100687 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100688
Vinay Sajipce7c9782011-05-17 07:15:53 +0100689 :param addr: A (host, port) tuple which the server listens on.
690 You can specify a port value of zero: the server's
691 *port* attribute will hold the actual port number
692 used, which can be used in client connections.
693 :param handler: A callable which will be called to process
694 incoming messages. The handler will be passed
695 the client address tuple, who the message is from,
696 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100697 :param poll_interval: The interval, in seconds, used in the underlying
698 :func:`select` or :func:`poll` call by
699 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100700 :param sockmap: A dictionary which will be used to hold
701 :class:`asyncore.dispatcher` instances used by
702 :func:`asyncore.loop`. This avoids changing the
703 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100704 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100705 channel_class = TestSMTPChannel
706
707 def __init__(self, addr, handler, poll_interval, sockmap):
708 self._localaddr = addr
709 self._remoteaddr = None
R David Murrayd1a30c92012-05-26 14:33:59 -0400710 self.data_size_limit = None
Vinay Sajipce7c9782011-05-17 07:15:53 +0100711 self.sockmap = sockmap
712 asyncore.dispatcher.__init__(self, map=sockmap)
713 try:
714 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
715 sock.setblocking(0)
716 self.set_socket(sock, map=sockmap)
717 # try to re-use a server port if possible
718 self.set_reuse_addr()
719 self.bind(addr)
720 self.port = sock.getsockname()[1]
721 self.listen(5)
722 except:
723 self.close()
Vinay Sajipe73afad2011-05-10 07:48:28 +0100724 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +0100725 self._handler = handler
726 self._thread = None
727 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100728
Vinay Sajipce7c9782011-05-17 07:15:53 +0100729 def handle_accepted(self, conn, addr):
730 """
731 Redefined only because the base class does not pass in a
732 map, forcing use of a global in :mod:`asyncore`.
733 """
734 channel = self.channel_class(self, conn, addr, self.sockmap)
Vinay Sajip314b92b2011-05-02 14:31:16 +0100735
Vinay Sajipce7c9782011-05-17 07:15:53 +0100736 def process_message(self, peer, mailfrom, rcpttos, data):
737 """
738 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100739
Vinay Sajipce7c9782011-05-17 07:15:53 +0100740 Typically, this will be a test case method.
741 :param peer: The client (host, port) tuple.
742 :param mailfrom: The address of the sender.
743 :param rcpttos: The addresses of the recipients.
744 :param data: The message.
745 """
746 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100747
Vinay Sajipce7c9782011-05-17 07:15:53 +0100748 def start(self):
749 """
750 Start the server running on a separate daemon thread.
751 """
752 self._thread = t = threading.Thread(target=self.serve_forever,
753 args=(self.poll_interval,))
754 t.setDaemon(True)
755 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100756
Vinay Sajipce7c9782011-05-17 07:15:53 +0100757 def serve_forever(self, poll_interval):
758 """
759 Run the :mod:`asyncore` loop until normal termination
760 conditions arise.
761 :param poll_interval: The interval, in seconds, used in the underlying
762 :func:`select` or :func:`poll` call by
763 :func:`asyncore.loop`.
764 """
765 try:
766 asyncore.loop(poll_interval, map=self.sockmap)
767 except select.error:
768 # On FreeBSD 8, closing the server repeatably
769 # raises this error. We swallow it if the
770 # server has been closed.
771 if self.connected or self.accepting:
772 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100773
Vinay Sajipce7c9782011-05-17 07:15:53 +0100774 def stop(self, timeout=None):
775 """
776 Stop the thread by closing the server instance.
777 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100778
Vinay Sajipce7c9782011-05-17 07:15:53 +0100779 :param timeout: How long to wait for the server thread
780 to terminate.
781 """
782 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100783 self._thread.join(timeout)
784 self._thread = None
Vinay Sajip7367d082011-05-02 13:17:27 +0100785
Vinay Sajipce7c9782011-05-17 07:15:53 +0100786 class ControlMixin(object):
787 """
788 This mixin is used to start a server on a separate thread, and
789 shut it down programmatically. Request handling is simplified - instead
790 of needing to derive a suitable RequestHandler subclass, you just
791 provide a callable which will be passed each received request to be
792 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100793
Vinay Sajipce7c9782011-05-17 07:15:53 +0100794 :param handler: A handler callable which will be called with a
795 single parameter - the request - in order to
796 process the request. This handler is called on the
797 server thread, effectively meaning that requests are
798 processed serially. While not quite Web scale ;-),
799 this should be fine for testing applications.
800 :param poll_interval: The polling interval in seconds.
801 """
802 def __init__(self, handler, poll_interval):
803 self._thread = None
804 self.poll_interval = poll_interval
805 self._handler = handler
806 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100807
Vinay Sajipce7c9782011-05-17 07:15:53 +0100808 def start(self):
809 """
810 Create a daemon thread to run the server, and start it.
811 """
812 self._thread = t = threading.Thread(target=self.serve_forever,
813 args=(self.poll_interval,))
814 t.setDaemon(True)
815 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100816
Vinay Sajipce7c9782011-05-17 07:15:53 +0100817 def serve_forever(self, poll_interval):
818 """
819 Run the server. Set the ready flag before entering the
820 service loop.
821 """
822 self.ready.set()
823 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100824
Vinay Sajipce7c9782011-05-17 07:15:53 +0100825 def stop(self, timeout=None):
826 """
827 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100828
Vinay Sajipce7c9782011-05-17 07:15:53 +0100829 :param timeout: How long to wait for the server thread
830 to terminate.
831 """
832 self.shutdown()
833 if self._thread is not None:
834 self._thread.join(timeout)
835 self._thread = None
836 self.server_close()
837 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100838
Vinay Sajipce7c9782011-05-17 07:15:53 +0100839 class TestHTTPServer(ControlMixin, HTTPServer):
840 """
841 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100842
Vinay Sajipce7c9782011-05-17 07:15:53 +0100843 :param addr: A tuple with the IP address and port to listen on.
844 :param handler: A handler callable which will be called with a
845 single parameter - the request - in order to
846 process the request.
847 :param poll_interval: The polling interval in seconds.
848 :param log: Pass ``True`` to enable log messages.
849 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100850 def __init__(self, addr, handler, poll_interval=0.5,
851 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100852 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
853 def __getattr__(self, name, default=None):
854 if name.startswith('do_'):
855 return self.process_request
856 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100857
Vinay Sajipce7c9782011-05-17 07:15:53 +0100858 def process_request(self):
859 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100860
Vinay Sajipce7c9782011-05-17 07:15:53 +0100861 def log_message(self, format, *args):
862 if log:
863 super(DelegatingHTTPRequestHandler,
864 self).log_message(format, *args)
865 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
866 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100867 self.sslctx = sslctx
868
869 def get_request(self):
870 try:
871 sock, addr = self.socket.accept()
872 if self.sslctx:
873 sock = self.sslctx.wrap_socket(sock, server_side=True)
874 except socket.error as e:
875 # socket errors are silenced by the caller, print them here
876 sys.stderr.write("Got an error:\n%s\n" % e)
877 raise
878 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100879
Vinay Sajipce7c9782011-05-17 07:15:53 +0100880 class TestTCPServer(ControlMixin, ThreadingTCPServer):
881 """
882 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100883
Vinay Sajipce7c9782011-05-17 07:15:53 +0100884 :param addr: A tuple with the IP address and port to listen on.
885 :param handler: A handler callable which will be called with a single
886 parameter - the request - in order to process the request.
887 :param poll_interval: The polling interval in seconds.
888 :bind_and_activate: If True (the default), binds the server and starts it
889 listening. If False, you need to call
890 :meth:`server_bind` and :meth:`server_activate` at
891 some later time before calling :meth:`start`, so that
892 the server will set up the socket and listen on it.
893 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100894
Vinay Sajipce7c9782011-05-17 07:15:53 +0100895 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100896
Vinay Sajipce7c9782011-05-17 07:15:53 +0100897 def __init__(self, addr, handler, poll_interval=0.5,
898 bind_and_activate=True):
899 class DelegatingTCPRequestHandler(StreamRequestHandler):
900
901 def handle(self):
902 self.server._handler(self)
903 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
904 bind_and_activate)
905 ControlMixin.__init__(self, handler, poll_interval)
906
907 def server_bind(self):
908 super(TestTCPServer, self).server_bind()
909 self.port = self.socket.getsockname()[1]
910
911 class TestUDPServer(ControlMixin, ThreadingUDPServer):
912 """
913 A UDP server which is controllable using :class:`ControlMixin`.
914
915 :param addr: A tuple with the IP address and port to listen on.
916 :param handler: A handler callable which will be called with a
917 single parameter - the request - in order to
918 process the request.
919 :param poll_interval: The polling interval for shutdown requests,
920 in seconds.
921 :bind_and_activate: If True (the default), binds the server and
922 starts it listening. If False, you need to
923 call :meth:`server_bind` and
924 :meth:`server_activate` at some later time
925 before calling :meth:`start`, so that the server will
926 set up the socket and listen on it.
927 """
Vinay Sajip3ef12292011-05-23 23:00:42 +0100928 def __init__(self, addr, handler, poll_interval=0.5,
929 bind_and_activate=True):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100930 class DelegatingUDPRequestHandler(DatagramRequestHandler):
931
932 def handle(self):
933 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100934
935 def finish(self):
936 data = self.wfile.getvalue()
Vinay Sajip3ef12292011-05-23 23:00:42 +0100937 if data:
938 try:
939 super(DelegatingUDPRequestHandler, self).finish()
940 except socket.error:
941 if not self.server._closed:
942 raise
Vinay Sajipba980db2011-05-23 21:37:54 +0100943
Vinay Sajip3ef12292011-05-23 23:00:42 +0100944 ThreadingUDPServer.__init__(self, addr,
945 DelegatingUDPRequestHandler,
Vinay Sajipce7c9782011-05-17 07:15:53 +0100946 bind_and_activate)
947 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100948 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100949
950 def server_bind(self):
951 super(TestUDPServer, self).server_bind()
952 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100953
Vinay Sajipba980db2011-05-23 21:37:54 +0100954 def server_close(self):
955 super(TestUDPServer, self).server_close()
956 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100957
958# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100959
Vinay Sajipce7c9782011-05-17 07:15:53 +0100960@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100961class SMTPHandlerTest(BaseTest):
Vinay Sajip827f5d32013-12-03 11:28:55 +0000962 TIMEOUT = 8.0
Vinay Sajipa463d252011-04-30 21:52:48 +0100963 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100964 sockmap = {}
965 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
966 sockmap)
967 server.start()
968 addr = ('localhost', server.port)
Vinay Sajip827f5d32013-12-03 11:28:55 +0000969 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log',
970 timeout=self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +0100971 self.assertEqual(h.toaddrs, ['you'])
972 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100973 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100974 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100975 h.handle(r)
Vinay Sajip827f5d32013-12-03 11:28:55 +0000976 self.handled.wait(self.TIMEOUT) # 14314: don't wait forever
Vinay Sajipa463d252011-04-30 21:52:48 +0100977 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +0000978 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +0100979 self.assertEqual(len(self.messages), 1)
980 peer, mailfrom, rcpttos, data = self.messages[0]
981 self.assertEqual(mailfrom, 'me')
982 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100983 self.assertIn('\nSubject: Log\n', data)
Vinay Sajipa463d252011-04-30 21:52:48 +0100984 self.assertTrue(data.endswith('\n\nHello'))
985 h.close()
986
987 def process_message(self, *args):
988 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100989 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100990
Christian Heimes180510d2008-03-03 19:15:45 +0000991class MemoryHandlerTest(BaseTest):
992
993 """Tests for the MemoryHandler."""
994
995 # Do not bother with a logger name group.
996 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
997
998 def setUp(self):
999 BaseTest.setUp(self)
1000 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1001 self.root_hdlr)
1002 self.mem_logger = logging.getLogger('mem')
1003 self.mem_logger.propagate = 0
1004 self.mem_logger.addHandler(self.mem_hdlr)
1005
1006 def tearDown(self):
1007 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001008 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001009
1010 def test_flush(self):
1011 # The memory handler flushes to its target handler based on specific
1012 # criteria (message count and message level).
1013 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001014 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001015 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001016 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001017 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001018 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001019 lines = [
1020 ('DEBUG', '1'),
1021 ('INFO', '2'),
1022 ('WARNING', '3'),
1023 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001024 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001025 for n in (4, 14):
1026 for i in range(9):
1027 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001028 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001029 # This will flush because it's the 10th message since the last
1030 # flush.
1031 self.mem_logger.debug(self.next_message())
1032 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001033 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001034
1035 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001036 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001037
1038
1039class ExceptionFormatter(logging.Formatter):
1040 """A special exception formatter."""
1041 def formatException(self, ei):
1042 return "Got a [%s]" % ei[0].__name__
1043
1044
1045class ConfigFileTest(BaseTest):
1046
1047 """Reading logging config from a .ini-style config file."""
1048
1049 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1050
1051 # config0 is a standard configuration.
1052 config0 = """
1053 [loggers]
1054 keys=root
1055
1056 [handlers]
1057 keys=hand1
1058
1059 [formatters]
1060 keys=form1
1061
1062 [logger_root]
1063 level=WARNING
1064 handlers=hand1
1065
1066 [handler_hand1]
1067 class=StreamHandler
1068 level=NOTSET
1069 formatter=form1
1070 args=(sys.stdout,)
1071
1072 [formatter_form1]
1073 format=%(levelname)s ++ %(message)s
1074 datefmt=
1075 """
1076
1077 # config1 adds a little to the standard configuration.
1078 config1 = """
1079 [loggers]
1080 keys=root,parser
1081
1082 [handlers]
1083 keys=hand1
1084
1085 [formatters]
1086 keys=form1
1087
1088 [logger_root]
1089 level=WARNING
1090 handlers=
1091
1092 [logger_parser]
1093 level=DEBUG
1094 handlers=hand1
1095 propagate=1
1096 qualname=compiler.parser
1097
1098 [handler_hand1]
1099 class=StreamHandler
1100 level=NOTSET
1101 formatter=form1
1102 args=(sys.stdout,)
1103
1104 [formatter_form1]
1105 format=%(levelname)s ++ %(message)s
1106 datefmt=
1107 """
1108
Vinay Sajip3f84b072011-03-07 17:49:33 +00001109 # config1a moves the handler to the root.
1110 config1a = """
1111 [loggers]
1112 keys=root,parser
1113
1114 [handlers]
1115 keys=hand1
1116
1117 [formatters]
1118 keys=form1
1119
1120 [logger_root]
1121 level=WARNING
1122 handlers=hand1
1123
1124 [logger_parser]
1125 level=DEBUG
1126 handlers=
1127 propagate=1
1128 qualname=compiler.parser
1129
1130 [handler_hand1]
1131 class=StreamHandler
1132 level=NOTSET
1133 formatter=form1
1134 args=(sys.stdout,)
1135
1136 [formatter_form1]
1137 format=%(levelname)s ++ %(message)s
1138 datefmt=
1139 """
1140
Christian Heimes180510d2008-03-03 19:15:45 +00001141 # config2 has a subtle configuration error that should be reported
1142 config2 = config1.replace("sys.stdout", "sys.stbout")
1143
1144 # config3 has a less subtle configuration error
1145 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1146
1147 # config4 specifies a custom formatter class to be loaded
1148 config4 = """
1149 [loggers]
1150 keys=root
1151
1152 [handlers]
1153 keys=hand1
1154
1155 [formatters]
1156 keys=form1
1157
1158 [logger_root]
1159 level=NOTSET
1160 handlers=hand1
1161
1162 [handler_hand1]
1163 class=StreamHandler
1164 level=NOTSET
1165 formatter=form1
1166 args=(sys.stdout,)
1167
1168 [formatter_form1]
1169 class=""" + __name__ + """.ExceptionFormatter
1170 format=%(levelname)s:%(name)s:%(message)s
1171 datefmt=
1172 """
1173
Georg Brandl3dbca812008-07-23 16:10:53 +00001174 # config5 specifies a custom handler class to be loaded
1175 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1176
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001177 # config6 uses ', ' delimiters in the handlers and formatters sections
1178 config6 = """
1179 [loggers]
1180 keys=root,parser
1181
1182 [handlers]
1183 keys=hand1, hand2
1184
1185 [formatters]
1186 keys=form1, form2
1187
1188 [logger_root]
1189 level=WARNING
1190 handlers=
1191
1192 [logger_parser]
1193 level=DEBUG
1194 handlers=hand1
1195 propagate=1
1196 qualname=compiler.parser
1197
1198 [handler_hand1]
1199 class=StreamHandler
1200 level=NOTSET
1201 formatter=form1
1202 args=(sys.stdout,)
1203
1204 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001205 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001206 level=NOTSET
1207 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001208 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001209
1210 [formatter_form1]
1211 format=%(levelname)s ++ %(message)s
1212 datefmt=
1213
1214 [formatter_form2]
1215 format=%(message)s
1216 datefmt=
1217 """
1218
Vinay Sajip3f84b072011-03-07 17:49:33 +00001219 # config7 adds a compiler logger.
1220 config7 = """
1221 [loggers]
1222 keys=root,parser,compiler
1223
1224 [handlers]
1225 keys=hand1
1226
1227 [formatters]
1228 keys=form1
1229
1230 [logger_root]
1231 level=WARNING
1232 handlers=hand1
1233
1234 [logger_compiler]
1235 level=DEBUG
1236 handlers=
1237 propagate=1
1238 qualname=compiler
1239
1240 [logger_parser]
1241 level=DEBUG
1242 handlers=
1243 propagate=1
1244 qualname=compiler.parser
1245
1246 [handler_hand1]
1247 class=StreamHandler
1248 level=NOTSET
1249 formatter=form1
1250 args=(sys.stdout,)
1251
1252 [formatter_form1]
1253 format=%(levelname)s ++ %(message)s
1254 datefmt=
1255 """
1256
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001257 disable_test = """
1258 [loggers]
1259 keys=root
1260
1261 [handlers]
1262 keys=screen
1263
1264 [formatters]
1265 keys=
1266
1267 [logger_root]
1268 level=DEBUG
1269 handlers=screen
1270
1271 [handler_screen]
1272 level=DEBUG
1273 class=StreamHandler
1274 args=(sys.stdout,)
1275 formatter=
1276 """
1277
1278 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001279 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001280 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001281
1282 def test_config0_ok(self):
1283 # A simple config file which overrides the default settings.
1284 with captured_stdout() as output:
1285 self.apply_config(self.config0)
1286 logger = logging.getLogger()
1287 # Won't output anything
1288 logger.info(self.next_message())
1289 # Outputs a message
1290 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001291 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001292 ('ERROR', '2'),
1293 ], stream=output)
1294 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001295 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001296
Georg Brandl3dbca812008-07-23 16:10:53 +00001297 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001298 # A config file defining a sub-parser as well.
1299 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001300 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001301 logger = logging.getLogger("compiler.parser")
1302 # Both will output a message
1303 logger.info(self.next_message())
1304 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001305 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001306 ('INFO', '1'),
1307 ('ERROR', '2'),
1308 ], stream=output)
1309 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001310 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001311
1312 def test_config2_failure(self):
1313 # A simple config file which overrides the default settings.
1314 self.assertRaises(Exception, self.apply_config, self.config2)
1315
1316 def test_config3_failure(self):
1317 # A simple config file which overrides the default settings.
1318 self.assertRaises(Exception, self.apply_config, self.config3)
1319
1320 def test_config4_ok(self):
1321 # A config file specifying a custom formatter class.
1322 with captured_stdout() as output:
1323 self.apply_config(self.config4)
1324 logger = logging.getLogger()
1325 try:
1326 raise RuntimeError()
1327 except RuntimeError:
1328 logging.exception("just testing")
1329 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001330 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001331 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1332 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001333 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001334
Georg Brandl3dbca812008-07-23 16:10:53 +00001335 def test_config5_ok(self):
1336 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001337
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001338 def test_config6_ok(self):
1339 self.test_config1_ok(config=self.config6)
1340
Vinay Sajip3f84b072011-03-07 17:49:33 +00001341 def test_config7_ok(self):
1342 with captured_stdout() as output:
1343 self.apply_config(self.config1a)
1344 logger = logging.getLogger("compiler.parser")
1345 # See issue #11424. compiler-hyphenated sorts
1346 # between compiler and compiler.xyz and this
1347 # was preventing compiler.xyz from being included
1348 # in the child loggers of compiler because of an
1349 # overzealous loop termination condition.
1350 hyphenated = logging.getLogger('compiler-hyphenated')
1351 # All will output a message
1352 logger.info(self.next_message())
1353 logger.error(self.next_message())
1354 hyphenated.critical(self.next_message())
1355 self.assert_log_lines([
1356 ('INFO', '1'),
1357 ('ERROR', '2'),
1358 ('CRITICAL', '3'),
1359 ], stream=output)
1360 # Original logger output is empty.
1361 self.assert_log_lines([])
1362 with captured_stdout() as output:
1363 self.apply_config(self.config7)
1364 logger = logging.getLogger("compiler.parser")
1365 self.assertFalse(logger.disabled)
1366 # Both will output a message
1367 logger.info(self.next_message())
1368 logger.error(self.next_message())
1369 logger = logging.getLogger("compiler.lexer")
1370 # Both will output a message
1371 logger.info(self.next_message())
1372 logger.error(self.next_message())
1373 # Will not appear
1374 hyphenated.critical(self.next_message())
1375 self.assert_log_lines([
1376 ('INFO', '4'),
1377 ('ERROR', '5'),
1378 ('INFO', '6'),
1379 ('ERROR', '7'),
1380 ], stream=output)
1381 # Original logger output is empty.
1382 self.assert_log_lines([])
1383
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001384 def test_logger_disabling(self):
1385 self.apply_config(self.disable_test)
1386 logger = logging.getLogger('foo')
1387 self.assertFalse(logger.disabled)
1388 self.apply_config(self.disable_test)
1389 self.assertTrue(logger.disabled)
1390 self.apply_config(self.disable_test, disable_existing_loggers=False)
1391 self.assertFalse(logger.disabled)
1392
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001393
Victor Stinner45df8202010-04-28 22:31:17 +00001394@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001395class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001396
Christian Heimes180510d2008-03-03 19:15:45 +00001397 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001398
Christian Heimes180510d2008-03-03 19:15:45 +00001399 def setUp(self):
1400 """Set up a TCP server to receive log messages, and a SocketHandler
1401 pointing to that server's address and port."""
1402 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001403 addr = ('localhost', 0)
1404 self.server = server = TestTCPServer(addr, self.handle_socket,
1405 0.01)
1406 server.start()
1407 server.ready.wait()
1408 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1409 server.port)
1410 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001411 self.root_logger.removeHandler(self.root_logger.handlers[0])
1412 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001413 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001414
Christian Heimes180510d2008-03-03 19:15:45 +00001415 def tearDown(self):
1416 """Shutdown the TCP server."""
1417 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001418 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001419 self.root_logger.removeHandler(self.sock_hdlr)
1420 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001421 finally:
1422 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001423
Vinay Sajip7367d082011-05-02 13:17:27 +01001424 def handle_socket(self, request):
1425 conn = request.connection
1426 while True:
1427 chunk = conn.recv(4)
1428 if len(chunk) < 4:
1429 break
1430 slen = struct.unpack(">L", chunk)[0]
1431 chunk = conn.recv(slen)
1432 while len(chunk) < slen:
1433 chunk = chunk + conn.recv(slen - len(chunk))
1434 obj = pickle.loads(chunk)
1435 record = logging.makeLogRecord(obj)
1436 self.log_output += record.msg + '\n'
1437 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001438
Christian Heimes180510d2008-03-03 19:15:45 +00001439 def test_output(self):
1440 # The log message sent to the SocketHandler is properly received.
1441 logger = logging.getLogger("tcp")
1442 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001443 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001444 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001445 self.handled.acquire()
1446 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001447
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001448 def test_noserver(self):
1449 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001450 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001451 #The logging call should try to connect, which should fail
1452 try:
1453 raise RuntimeError('Deliberate mistake')
1454 except RuntimeError:
1455 self.root_logger.exception('Never sent')
1456 self.root_logger.error('Never sent, either')
1457 now = time.time()
1458 self.assertTrue(self.sock_hdlr.retryTime > now)
1459 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1460 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001461
Vinay Sajip7367d082011-05-02 13:17:27 +01001462
1463@unittest.skipUnless(threading, 'Threading required for this test.')
1464class DatagramHandlerTest(BaseTest):
1465
1466 """Test for DatagramHandler."""
1467
1468 def setUp(self):
1469 """Set up a UDP server to receive log messages, and a DatagramHandler
1470 pointing to that server's address and port."""
1471 BaseTest.setUp(self)
1472 addr = ('localhost', 0)
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001473 self.server = server = TestUDPServer(addr, self.handle_datagram, 0.01)
Vinay Sajip7367d082011-05-02 13:17:27 +01001474 server.start()
1475 server.ready.wait()
1476 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1477 server.port)
1478 self.log_output = ''
1479 self.root_logger.removeHandler(self.root_logger.handlers[0])
1480 self.root_logger.addHandler(self.sock_hdlr)
1481 self.handled = threading.Event()
1482
1483 def tearDown(self):
1484 """Shutdown the UDP server."""
1485 try:
1486 self.server.stop(2.0)
1487 self.root_logger.removeHandler(self.sock_hdlr)
1488 self.sock_hdlr.close()
1489 finally:
1490 BaseTest.tearDown(self)
1491
1492 def handle_datagram(self, request):
1493 slen = struct.pack('>L', 0) # length of prefix
1494 packet = request.packet[len(slen):]
1495 obj = pickle.loads(packet)
1496 record = logging.makeLogRecord(obj)
1497 self.log_output += record.msg + '\n'
1498 self.handled.set()
1499
1500 def test_output(self):
1501 # The log message sent to the DatagramHandler is properly received.
1502 logger = logging.getLogger("udp")
1503 logger.error("spam")
1504 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001505 self.handled.clear()
1506 logger.error("eggs")
1507 self.handled.wait()
1508 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001509
1510
1511@unittest.skipUnless(threading, 'Threading required for this test.')
1512class SysLogHandlerTest(BaseTest):
1513
1514 """Test for SysLogHandler using UDP."""
1515
1516 def setUp(self):
1517 """Set up a UDP server to receive log messages, and a SysLogHandler
1518 pointing to that server's address and port."""
1519 BaseTest.setUp(self)
1520 addr = ('localhost', 0)
1521 self.server = server = TestUDPServer(addr, self.handle_datagram,
1522 0.01)
1523 server.start()
1524 server.ready.wait()
1525 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1526 server.port))
1527 self.log_output = ''
1528 self.root_logger.removeHandler(self.root_logger.handlers[0])
1529 self.root_logger.addHandler(self.sl_hdlr)
1530 self.handled = threading.Event()
1531
1532 def tearDown(self):
1533 """Shutdown the UDP server."""
1534 try:
1535 self.server.stop(2.0)
1536 self.root_logger.removeHandler(self.sl_hdlr)
1537 self.sl_hdlr.close()
1538 finally:
1539 BaseTest.tearDown(self)
1540
1541 def handle_datagram(self, request):
1542 self.log_output = request.packet
1543 self.handled.set()
1544
1545 def test_output(self):
1546 # The log message sent to the SysLogHandler is properly received.
1547 logger = logging.getLogger("slh")
1548 logger.error("sp\xe4m")
1549 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001550 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001551 self.handled.clear()
1552 self.sl_hdlr.append_nul = False
1553 logger.error("sp\xe4m")
1554 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001555 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001556 self.handled.clear()
1557 self.sl_hdlr.ident = "h\xe4m-"
1558 logger.error("sp\xe4m")
1559 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001560 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001561
1562
1563@unittest.skipUnless(threading, 'Threading required for this test.')
1564class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001565 """Test for HTTPHandler."""
1566
Vinay Sajip9ba87612011-05-21 11:32:15 +01001567 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1568MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1569zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1570aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1571AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
15720tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
15735LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1574piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
15752z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1576i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1577PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1578E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1579rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1580/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1581-----END RSA PRIVATE KEY-----
1582-----BEGIN CERTIFICATE-----
1583MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1584BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1585EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1586xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1587l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1588vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1589BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1590S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1591BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15924f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15931c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1594IRbTpvlHWPjsSvHz0ZOH
1595-----END CERTIFICATE-----"""
1596
Vinay Sajip7367d082011-05-02 13:17:27 +01001597 def setUp(self):
1598 """Set up an HTTP server to receive log messages, and a HTTPHandler
1599 pointing to that server's address and port."""
1600 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001601 self.handled = threading.Event()
1602
Vinay Sajip7367d082011-05-02 13:17:27 +01001603 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001604 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001605 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001606 if self.command == 'POST':
1607 try:
1608 rlen = int(request.headers['Content-Length'])
1609 self.post_data = request.rfile.read(rlen)
1610 except:
1611 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001612 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001613 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001614 self.handled.set()
1615
1616 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001617 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001618 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001619 root_logger = self.root_logger
1620 root_logger.removeHandler(self.root_logger.handlers[0])
1621 for secure in (False, True):
1622 addr = ('localhost', 0)
1623 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001624 try:
1625 import ssl
1626 fd, fn = tempfile.mkstemp()
1627 os.close(fd)
1628 with open(fn, 'w') as f:
1629 f.write(self.PEMFILE)
1630 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1631 sslctx.load_cert_chain(fn)
1632 os.unlink(fn)
1633 except ImportError:
1634 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001635 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001636 sslctx = None
1637 self.server = server = TestHTTPServer(addr, self.handle_request,
1638 0.01, sslctx=sslctx)
1639 server.start()
1640 server.ready.wait()
1641 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001642 secure_client = secure and sslctx
1643 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1644 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001645 self.log_data = None
1646 root_logger.addHandler(self.h_hdlr)
1647
1648 for method in ('GET', 'POST'):
1649 self.h_hdlr.method = method
1650 self.handled.clear()
1651 msg = "sp\xe4m"
1652 logger.error(msg)
1653 self.handled.wait()
1654 self.assertEqual(self.log_data.path, '/frob')
1655 self.assertEqual(self.command, method)
1656 if method == 'GET':
1657 d = parse_qs(self.log_data.query)
1658 else:
1659 d = parse_qs(self.post_data.decode('utf-8'))
1660 self.assertEqual(d['name'], ['http'])
1661 self.assertEqual(d['funcName'], ['test_output'])
1662 self.assertEqual(d['msg'], [msg])
1663
1664 self.server.stop(2.0)
1665 self.root_logger.removeHandler(self.h_hdlr)
1666 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001667
Christian Heimes180510d2008-03-03 19:15:45 +00001668class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001669
Christian Heimes180510d2008-03-03 19:15:45 +00001670 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001671
Christian Heimes180510d2008-03-03 19:15:45 +00001672 def setUp(self):
1673 """Create a dict to remember potentially destroyed objects."""
1674 BaseTest.setUp(self)
1675 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001676
Christian Heimes180510d2008-03-03 19:15:45 +00001677 def _watch_for_survival(self, *args):
1678 """Watch the given objects for survival, by creating weakrefs to
1679 them."""
1680 for obj in args:
1681 key = id(obj), repr(obj)
1682 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001683
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001684 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001685 """Assert that all objects watched for survival have survived."""
1686 # Trigger cycle breaking.
1687 gc.collect()
1688 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001689 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001690 if ref() is None:
1691 dead.append(repr_)
1692 if dead:
1693 self.fail("%d objects should have survived "
1694 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001695
Christian Heimes180510d2008-03-03 19:15:45 +00001696 def test_persistent_loggers(self):
1697 # Logger objects are persistent and retain their configuration, even
1698 # if visible references are destroyed.
1699 self.root_logger.setLevel(logging.INFO)
1700 foo = logging.getLogger("foo")
1701 self._watch_for_survival(foo)
1702 foo.setLevel(logging.DEBUG)
1703 self.root_logger.debug(self.next_message())
1704 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001705 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001706 ('foo', 'DEBUG', '2'),
1707 ])
1708 del foo
1709 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001710 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001711 # foo has retained its settings.
1712 bar = logging.getLogger("foo")
1713 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001714 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001715 ('foo', 'DEBUG', '2'),
1716 ('foo', 'DEBUG', '3'),
1717 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001718
Benjamin Petersonf91df042009-02-13 02:50:59 +00001719
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001720class EncodingTest(BaseTest):
1721 def test_encoding_plain_file(self):
1722 # In Python 2.x, a plain file object is treated as having no encoding.
1723 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001724 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1725 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001726 # the non-ascii data we write to the log.
1727 data = "foo\x80"
1728 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001729 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001730 log.addHandler(handler)
1731 try:
1732 # write non-ascii data to the log.
1733 log.warning(data)
1734 finally:
1735 log.removeHandler(handler)
1736 handler.close()
1737 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001738 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001739 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001740 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001741 finally:
1742 f.close()
1743 finally:
1744 if os.path.isfile(fn):
1745 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001746
Benjamin Petersonf91df042009-02-13 02:50:59 +00001747 def test_encoding_cyrillic_unicode(self):
1748 log = logging.getLogger("test")
1749 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1750 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1751 #Ensure it's written in a Cyrillic encoding
1752 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001753 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001754 stream = io.BytesIO()
1755 writer = writer_class(stream, 'strict')
1756 handler = logging.StreamHandler(writer)
1757 log.addHandler(handler)
1758 try:
1759 log.warning(message)
1760 finally:
1761 log.removeHandler(handler)
1762 handler.close()
1763 # check we wrote exactly those bytes, ignoring trailing \n etc
1764 s = stream.getvalue()
1765 #Compare against what the data should be when encoded in CP-1251
1766 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1767
1768
Georg Brandlf9734072008-12-07 15:30:06 +00001769class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001770
Georg Brandlf9734072008-12-07 15:30:06 +00001771 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001772 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001773 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001774 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001775 warnings.filterwarnings("always", category=UserWarning)
1776 stream = io.StringIO()
1777 h = logging.StreamHandler(stream)
1778 logger = logging.getLogger("py.warnings")
1779 logger.addHandler(h)
1780 warnings.warn("I'm warning you...")
1781 logger.removeHandler(h)
1782 s = stream.getvalue()
1783 h.close()
1784 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001785
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001786 #See if an explicit file uses the original implementation
1787 a_file = io.StringIO()
1788 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1789 a_file, "Dummy line")
1790 s = a_file.getvalue()
1791 a_file.close()
1792 self.assertEqual(s,
1793 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1794
1795 def test_warnings_no_handlers(self):
1796 with warnings.catch_warnings():
1797 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001798 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001799
1800 # confirm our assumption: no loggers are set
1801 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001802 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001803
1804 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001805 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001806 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001807
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001808
1809def formatFunc(format, datefmt=None):
1810 return logging.Formatter(format, datefmt)
1811
1812def handlerFunc():
1813 return logging.StreamHandler()
1814
1815class CustomHandler(logging.StreamHandler):
1816 pass
1817
1818class ConfigDictTest(BaseTest):
1819
1820 """Reading logging config from a dictionary."""
1821
1822 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1823
1824 # config0 is a standard configuration.
1825 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001826 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001827 'formatters': {
1828 'form1' : {
1829 'format' : '%(levelname)s ++ %(message)s',
1830 },
1831 },
1832 'handlers' : {
1833 'hand1' : {
1834 'class' : 'logging.StreamHandler',
1835 'formatter' : 'form1',
1836 'level' : 'NOTSET',
1837 'stream' : 'ext://sys.stdout',
1838 },
1839 },
1840 'root' : {
1841 'level' : 'WARNING',
1842 'handlers' : ['hand1'],
1843 },
1844 }
1845
1846 # config1 adds a little to the standard configuration.
1847 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001848 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001849 'formatters': {
1850 'form1' : {
1851 'format' : '%(levelname)s ++ %(message)s',
1852 },
1853 },
1854 'handlers' : {
1855 'hand1' : {
1856 'class' : 'logging.StreamHandler',
1857 'formatter' : 'form1',
1858 'level' : 'NOTSET',
1859 'stream' : 'ext://sys.stdout',
1860 },
1861 },
1862 'loggers' : {
1863 'compiler.parser' : {
1864 'level' : 'DEBUG',
1865 'handlers' : ['hand1'],
1866 },
1867 },
1868 'root' : {
1869 'level' : 'WARNING',
1870 },
1871 }
1872
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001873 # config1a moves the handler to the root. Used with config8a
1874 config1a = {
1875 'version': 1,
1876 'formatters': {
1877 'form1' : {
1878 'format' : '%(levelname)s ++ %(message)s',
1879 },
1880 },
1881 'handlers' : {
1882 'hand1' : {
1883 'class' : 'logging.StreamHandler',
1884 'formatter' : 'form1',
1885 'level' : 'NOTSET',
1886 'stream' : 'ext://sys.stdout',
1887 },
1888 },
1889 'loggers' : {
1890 'compiler.parser' : {
1891 'level' : 'DEBUG',
1892 },
1893 },
1894 'root' : {
1895 'level' : 'WARNING',
1896 'handlers' : ['hand1'],
1897 },
1898 }
1899
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001900 # config2 has a subtle configuration error that should be reported
1901 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001902 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001903 'formatters': {
1904 'form1' : {
1905 'format' : '%(levelname)s ++ %(message)s',
1906 },
1907 },
1908 'handlers' : {
1909 'hand1' : {
1910 'class' : 'logging.StreamHandler',
1911 'formatter' : 'form1',
1912 'level' : 'NOTSET',
1913 'stream' : 'ext://sys.stdbout',
1914 },
1915 },
1916 'loggers' : {
1917 'compiler.parser' : {
1918 'level' : 'DEBUG',
1919 'handlers' : ['hand1'],
1920 },
1921 },
1922 'root' : {
1923 'level' : 'WARNING',
1924 },
1925 }
1926
1927 #As config1 but with a misspelt level on a handler
1928 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001929 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001930 'formatters': {
1931 'form1' : {
1932 'format' : '%(levelname)s ++ %(message)s',
1933 },
1934 },
1935 'handlers' : {
1936 'hand1' : {
1937 'class' : 'logging.StreamHandler',
1938 'formatter' : 'form1',
1939 'level' : 'NTOSET',
1940 'stream' : 'ext://sys.stdout',
1941 },
1942 },
1943 'loggers' : {
1944 'compiler.parser' : {
1945 'level' : 'DEBUG',
1946 'handlers' : ['hand1'],
1947 },
1948 },
1949 'root' : {
1950 'level' : 'WARNING',
1951 },
1952 }
1953
1954
1955 #As config1 but with a misspelt level on a logger
1956 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001957 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001958 'formatters': {
1959 'form1' : {
1960 'format' : '%(levelname)s ++ %(message)s',
1961 },
1962 },
1963 'handlers' : {
1964 'hand1' : {
1965 'class' : 'logging.StreamHandler',
1966 'formatter' : 'form1',
1967 'level' : 'NOTSET',
1968 'stream' : 'ext://sys.stdout',
1969 },
1970 },
1971 'loggers' : {
1972 'compiler.parser' : {
1973 'level' : 'DEBUG',
1974 'handlers' : ['hand1'],
1975 },
1976 },
1977 'root' : {
1978 'level' : 'WRANING',
1979 },
1980 }
1981
1982 # config3 has a less subtle configuration error
1983 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001984 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001985 'formatters': {
1986 'form1' : {
1987 'format' : '%(levelname)s ++ %(message)s',
1988 },
1989 },
1990 'handlers' : {
1991 'hand1' : {
1992 'class' : 'logging.StreamHandler',
1993 'formatter' : 'misspelled_name',
1994 'level' : 'NOTSET',
1995 'stream' : 'ext://sys.stdout',
1996 },
1997 },
1998 'loggers' : {
1999 'compiler.parser' : {
2000 'level' : 'DEBUG',
2001 'handlers' : ['hand1'],
2002 },
2003 },
2004 'root' : {
2005 'level' : 'WARNING',
2006 },
2007 }
2008
2009 # config4 specifies a custom formatter class to be loaded
2010 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002011 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002012 'formatters': {
2013 'form1' : {
2014 '()' : __name__ + '.ExceptionFormatter',
2015 'format' : '%(levelname)s:%(name)s:%(message)s',
2016 },
2017 },
2018 'handlers' : {
2019 'hand1' : {
2020 'class' : 'logging.StreamHandler',
2021 'formatter' : 'form1',
2022 'level' : 'NOTSET',
2023 'stream' : 'ext://sys.stdout',
2024 },
2025 },
2026 'root' : {
2027 'level' : 'NOTSET',
2028 'handlers' : ['hand1'],
2029 },
2030 }
2031
2032 # As config4 but using an actual callable rather than a string
2033 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002034 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002035 'formatters': {
2036 'form1' : {
2037 '()' : ExceptionFormatter,
2038 'format' : '%(levelname)s:%(name)s:%(message)s',
2039 },
2040 'form2' : {
2041 '()' : __name__ + '.formatFunc',
2042 'format' : '%(levelname)s:%(name)s:%(message)s',
2043 },
2044 'form3' : {
2045 '()' : formatFunc,
2046 'format' : '%(levelname)s:%(name)s:%(message)s',
2047 },
2048 },
2049 'handlers' : {
2050 'hand1' : {
2051 'class' : 'logging.StreamHandler',
2052 'formatter' : 'form1',
2053 'level' : 'NOTSET',
2054 'stream' : 'ext://sys.stdout',
2055 },
2056 'hand2' : {
2057 '()' : handlerFunc,
2058 },
2059 },
2060 'root' : {
2061 'level' : 'NOTSET',
2062 'handlers' : ['hand1'],
2063 },
2064 }
2065
2066 # config5 specifies a custom handler class to be loaded
2067 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002068 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002069 'formatters': {
2070 'form1' : {
2071 'format' : '%(levelname)s ++ %(message)s',
2072 },
2073 },
2074 'handlers' : {
2075 'hand1' : {
2076 'class' : __name__ + '.CustomHandler',
2077 'formatter' : 'form1',
2078 'level' : 'NOTSET',
2079 'stream' : 'ext://sys.stdout',
2080 },
2081 },
2082 'loggers' : {
2083 'compiler.parser' : {
2084 'level' : 'DEBUG',
2085 'handlers' : ['hand1'],
2086 },
2087 },
2088 'root' : {
2089 'level' : 'WARNING',
2090 },
2091 }
2092
2093 # config6 specifies a custom handler class to be loaded
2094 # but has bad arguments
2095 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002096 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002097 'formatters': {
2098 'form1' : {
2099 'format' : '%(levelname)s ++ %(message)s',
2100 },
2101 },
2102 'handlers' : {
2103 'hand1' : {
2104 'class' : __name__ + '.CustomHandler',
2105 'formatter' : 'form1',
2106 'level' : 'NOTSET',
2107 'stream' : 'ext://sys.stdout',
2108 '9' : 'invalid parameter name',
2109 },
2110 },
2111 'loggers' : {
2112 'compiler.parser' : {
2113 'level' : 'DEBUG',
2114 'handlers' : ['hand1'],
2115 },
2116 },
2117 'root' : {
2118 'level' : 'WARNING',
2119 },
2120 }
2121
2122 #config 7 does not define compiler.parser but defines compiler.lexer
2123 #so compiler.parser should be disabled after applying it
2124 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002125 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002126 'formatters': {
2127 'form1' : {
2128 'format' : '%(levelname)s ++ %(message)s',
2129 },
2130 },
2131 'handlers' : {
2132 'hand1' : {
2133 'class' : 'logging.StreamHandler',
2134 'formatter' : 'form1',
2135 'level' : 'NOTSET',
2136 'stream' : 'ext://sys.stdout',
2137 },
2138 },
2139 'loggers' : {
2140 'compiler.lexer' : {
2141 'level' : 'DEBUG',
2142 'handlers' : ['hand1'],
2143 },
2144 },
2145 'root' : {
2146 'level' : 'WARNING',
2147 },
2148 }
2149
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002150 # config8 defines both compiler and compiler.lexer
2151 # so compiler.parser should not be disabled (since
2152 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002153 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002154 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002155 'disable_existing_loggers' : False,
2156 'formatters': {
2157 'form1' : {
2158 'format' : '%(levelname)s ++ %(message)s',
2159 },
2160 },
2161 'handlers' : {
2162 'hand1' : {
2163 'class' : 'logging.StreamHandler',
2164 'formatter' : 'form1',
2165 'level' : 'NOTSET',
2166 'stream' : 'ext://sys.stdout',
2167 },
2168 },
2169 'loggers' : {
2170 'compiler' : {
2171 'level' : 'DEBUG',
2172 'handlers' : ['hand1'],
2173 },
2174 'compiler.lexer' : {
2175 },
2176 },
2177 'root' : {
2178 'level' : 'WARNING',
2179 },
2180 }
2181
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002182 # config8a disables existing loggers
2183 config8a = {
2184 'version': 1,
2185 'disable_existing_loggers' : True,
2186 'formatters': {
2187 'form1' : {
2188 'format' : '%(levelname)s ++ %(message)s',
2189 },
2190 },
2191 'handlers' : {
2192 'hand1' : {
2193 'class' : 'logging.StreamHandler',
2194 'formatter' : 'form1',
2195 'level' : 'NOTSET',
2196 'stream' : 'ext://sys.stdout',
2197 },
2198 },
2199 'loggers' : {
2200 'compiler' : {
2201 'level' : 'DEBUG',
2202 'handlers' : ['hand1'],
2203 },
2204 'compiler.lexer' : {
2205 },
2206 },
2207 'root' : {
2208 'level' : 'WARNING',
2209 },
2210 }
2211
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002212 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002213 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002214 'formatters': {
2215 'form1' : {
2216 'format' : '%(levelname)s ++ %(message)s',
2217 },
2218 },
2219 'handlers' : {
2220 'hand1' : {
2221 'class' : 'logging.StreamHandler',
2222 'formatter' : 'form1',
2223 'level' : 'WARNING',
2224 'stream' : 'ext://sys.stdout',
2225 },
2226 },
2227 'loggers' : {
2228 'compiler.parser' : {
2229 'level' : 'WARNING',
2230 'handlers' : ['hand1'],
2231 },
2232 },
2233 'root' : {
2234 'level' : 'NOTSET',
2235 },
2236 }
2237
2238 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002239 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002240 'incremental' : True,
2241 'handlers' : {
2242 'hand1' : {
2243 'level' : 'WARNING',
2244 },
2245 },
2246 'loggers' : {
2247 'compiler.parser' : {
2248 'level' : 'INFO',
2249 },
2250 },
2251 }
2252
2253 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002254 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002255 'incremental' : True,
2256 'handlers' : {
2257 'hand1' : {
2258 'level' : 'INFO',
2259 },
2260 },
2261 'loggers' : {
2262 'compiler.parser' : {
2263 'level' : 'INFO',
2264 },
2265 },
2266 }
2267
2268 #As config1 but with a filter added
2269 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002270 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002271 'formatters': {
2272 'form1' : {
2273 'format' : '%(levelname)s ++ %(message)s',
2274 },
2275 },
2276 'filters' : {
2277 'filt1' : {
2278 'name' : 'compiler.parser',
2279 },
2280 },
2281 'handlers' : {
2282 'hand1' : {
2283 'class' : 'logging.StreamHandler',
2284 'formatter' : 'form1',
2285 'level' : 'NOTSET',
2286 'stream' : 'ext://sys.stdout',
2287 'filters' : ['filt1'],
2288 },
2289 },
2290 'loggers' : {
2291 'compiler.parser' : {
2292 'level' : 'DEBUG',
2293 'filters' : ['filt1'],
2294 },
2295 },
2296 'root' : {
2297 'level' : 'WARNING',
2298 'handlers' : ['hand1'],
2299 },
2300 }
2301
2302 #As config1 but using cfg:// references
2303 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002304 'version': 1,
2305 'true_formatters': {
2306 'form1' : {
2307 'format' : '%(levelname)s ++ %(message)s',
2308 },
2309 },
2310 'handler_configs': {
2311 'hand1' : {
2312 'class' : 'logging.StreamHandler',
2313 'formatter' : 'form1',
2314 'level' : 'NOTSET',
2315 'stream' : 'ext://sys.stdout',
2316 },
2317 },
2318 'formatters' : 'cfg://true_formatters',
2319 'handlers' : {
2320 'hand1' : 'cfg://handler_configs[hand1]',
2321 },
2322 'loggers' : {
2323 'compiler.parser' : {
2324 'level' : 'DEBUG',
2325 'handlers' : ['hand1'],
2326 },
2327 },
2328 'root' : {
2329 'level' : 'WARNING',
2330 },
2331 }
2332
2333 #As config11 but missing the version key
2334 config12 = {
2335 'true_formatters': {
2336 'form1' : {
2337 'format' : '%(levelname)s ++ %(message)s',
2338 },
2339 },
2340 'handler_configs': {
2341 'hand1' : {
2342 'class' : 'logging.StreamHandler',
2343 'formatter' : 'form1',
2344 'level' : 'NOTSET',
2345 'stream' : 'ext://sys.stdout',
2346 },
2347 },
2348 'formatters' : 'cfg://true_formatters',
2349 'handlers' : {
2350 'hand1' : 'cfg://handler_configs[hand1]',
2351 },
2352 'loggers' : {
2353 'compiler.parser' : {
2354 'level' : 'DEBUG',
2355 'handlers' : ['hand1'],
2356 },
2357 },
2358 'root' : {
2359 'level' : 'WARNING',
2360 },
2361 }
2362
2363 #As config11 but using an unsupported version
2364 config13 = {
2365 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002366 'true_formatters': {
2367 'form1' : {
2368 'format' : '%(levelname)s ++ %(message)s',
2369 },
2370 },
2371 'handler_configs': {
2372 'hand1' : {
2373 'class' : 'logging.StreamHandler',
2374 'formatter' : 'form1',
2375 'level' : 'NOTSET',
2376 'stream' : 'ext://sys.stdout',
2377 },
2378 },
2379 'formatters' : 'cfg://true_formatters',
2380 'handlers' : {
2381 'hand1' : 'cfg://handler_configs[hand1]',
2382 },
2383 'loggers' : {
2384 'compiler.parser' : {
2385 'level' : 'DEBUG',
2386 'handlers' : ['hand1'],
2387 },
2388 },
2389 'root' : {
2390 'level' : 'WARNING',
2391 },
2392 }
2393
Vinay Sajip3f885b52013-03-22 15:19:54 +00002394 out_of_order = {
2395 "version": 1,
2396 "formatters": {
2397 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002398 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2399 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002400 }
2401 },
2402 "handlers": {
2403 "fileGlobal": {
2404 "class": "logging.StreamHandler",
2405 "level": "DEBUG",
2406 "formatter": "mySimpleFormatter"
2407 },
2408 "bufferGlobal": {
2409 "class": "logging.handlers.MemoryHandler",
2410 "capacity": 5,
2411 "formatter": "mySimpleFormatter",
2412 "target": "fileGlobal",
2413 "level": "DEBUG"
2414 }
2415 },
2416 "loggers": {
2417 "mymodule": {
2418 "level": "DEBUG",
2419 "handlers": ["bufferGlobal"],
2420 "propagate": "true"
2421 }
2422 }
2423 }
2424
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002425 def apply_config(self, conf):
2426 logging.config.dictConfig(conf)
2427
2428 def test_config0_ok(self):
2429 # A simple config which overrides the default settings.
2430 with captured_stdout() as output:
2431 self.apply_config(self.config0)
2432 logger = logging.getLogger()
2433 # Won't output anything
2434 logger.info(self.next_message())
2435 # Outputs a message
2436 logger.error(self.next_message())
2437 self.assert_log_lines([
2438 ('ERROR', '2'),
2439 ], stream=output)
2440 # Original logger output is empty.
2441 self.assert_log_lines([])
2442
2443 def test_config1_ok(self, config=config1):
2444 # A config defining a sub-parser as well.
2445 with captured_stdout() as output:
2446 self.apply_config(config)
2447 logger = logging.getLogger("compiler.parser")
2448 # Both will output a message
2449 logger.info(self.next_message())
2450 logger.error(self.next_message())
2451 self.assert_log_lines([
2452 ('INFO', '1'),
2453 ('ERROR', '2'),
2454 ], stream=output)
2455 # Original logger output is empty.
2456 self.assert_log_lines([])
2457
2458 def test_config2_failure(self):
2459 # A simple config which overrides the default settings.
2460 self.assertRaises(Exception, self.apply_config, self.config2)
2461
2462 def test_config2a_failure(self):
2463 # A simple config which overrides the default settings.
2464 self.assertRaises(Exception, self.apply_config, self.config2a)
2465
2466 def test_config2b_failure(self):
2467 # A simple config which overrides the default settings.
2468 self.assertRaises(Exception, self.apply_config, self.config2b)
2469
2470 def test_config3_failure(self):
2471 # A simple config which overrides the default settings.
2472 self.assertRaises(Exception, self.apply_config, self.config3)
2473
2474 def test_config4_ok(self):
2475 # A config specifying a custom formatter class.
2476 with captured_stdout() as output:
2477 self.apply_config(self.config4)
2478 #logger = logging.getLogger()
2479 try:
2480 raise RuntimeError()
2481 except RuntimeError:
2482 logging.exception("just testing")
2483 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002484 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002485 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2486 # Original logger output is empty
2487 self.assert_log_lines([])
2488
2489 def test_config4a_ok(self):
2490 # A config specifying a custom formatter class.
2491 with captured_stdout() as output:
2492 self.apply_config(self.config4a)
2493 #logger = logging.getLogger()
2494 try:
2495 raise RuntimeError()
2496 except RuntimeError:
2497 logging.exception("just testing")
2498 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002499 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002500 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2501 # Original logger output is empty
2502 self.assert_log_lines([])
2503
2504 def test_config5_ok(self):
2505 self.test_config1_ok(config=self.config5)
2506
2507 def test_config6_failure(self):
2508 self.assertRaises(Exception, self.apply_config, self.config6)
2509
2510 def test_config7_ok(self):
2511 with captured_stdout() as output:
2512 self.apply_config(self.config1)
2513 logger = logging.getLogger("compiler.parser")
2514 # Both will output a message
2515 logger.info(self.next_message())
2516 logger.error(self.next_message())
2517 self.assert_log_lines([
2518 ('INFO', '1'),
2519 ('ERROR', '2'),
2520 ], stream=output)
2521 # Original logger output is empty.
2522 self.assert_log_lines([])
2523 with captured_stdout() as output:
2524 self.apply_config(self.config7)
2525 logger = logging.getLogger("compiler.parser")
2526 self.assertTrue(logger.disabled)
2527 logger = logging.getLogger("compiler.lexer")
2528 # Both will output a message
2529 logger.info(self.next_message())
2530 logger.error(self.next_message())
2531 self.assert_log_lines([
2532 ('INFO', '3'),
2533 ('ERROR', '4'),
2534 ], stream=output)
2535 # Original logger output is empty.
2536 self.assert_log_lines([])
2537
2538 #Same as test_config_7_ok but don't disable old loggers.
2539 def test_config_8_ok(self):
2540 with captured_stdout() as output:
2541 self.apply_config(self.config1)
2542 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002543 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002544 logger.info(self.next_message())
2545 logger.error(self.next_message())
2546 self.assert_log_lines([
2547 ('INFO', '1'),
2548 ('ERROR', '2'),
2549 ], stream=output)
2550 # Original logger output is empty.
2551 self.assert_log_lines([])
2552 with captured_stdout() as output:
2553 self.apply_config(self.config8)
2554 logger = logging.getLogger("compiler.parser")
2555 self.assertFalse(logger.disabled)
2556 # Both will output a message
2557 logger.info(self.next_message())
2558 logger.error(self.next_message())
2559 logger = logging.getLogger("compiler.lexer")
2560 # Both will output a message
2561 logger.info(self.next_message())
2562 logger.error(self.next_message())
2563 self.assert_log_lines([
2564 ('INFO', '3'),
2565 ('ERROR', '4'),
2566 ('INFO', '5'),
2567 ('ERROR', '6'),
2568 ], stream=output)
2569 # Original logger output is empty.
2570 self.assert_log_lines([])
2571
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002572 def test_config_8a_ok(self):
2573 with captured_stdout() as output:
2574 self.apply_config(self.config1a)
2575 logger = logging.getLogger("compiler.parser")
2576 # See issue #11424. compiler-hyphenated sorts
2577 # between compiler and compiler.xyz and this
2578 # was preventing compiler.xyz from being included
2579 # in the child loggers of compiler because of an
2580 # overzealous loop termination condition.
2581 hyphenated = logging.getLogger('compiler-hyphenated')
2582 # All will output a message
2583 logger.info(self.next_message())
2584 logger.error(self.next_message())
2585 hyphenated.critical(self.next_message())
2586 self.assert_log_lines([
2587 ('INFO', '1'),
2588 ('ERROR', '2'),
2589 ('CRITICAL', '3'),
2590 ], stream=output)
2591 # Original logger output is empty.
2592 self.assert_log_lines([])
2593 with captured_stdout() as output:
2594 self.apply_config(self.config8a)
2595 logger = logging.getLogger("compiler.parser")
2596 self.assertFalse(logger.disabled)
2597 # Both will output a message
2598 logger.info(self.next_message())
2599 logger.error(self.next_message())
2600 logger = logging.getLogger("compiler.lexer")
2601 # Both will output a message
2602 logger.info(self.next_message())
2603 logger.error(self.next_message())
2604 # Will not appear
2605 hyphenated.critical(self.next_message())
2606 self.assert_log_lines([
2607 ('INFO', '4'),
2608 ('ERROR', '5'),
2609 ('INFO', '6'),
2610 ('ERROR', '7'),
2611 ], stream=output)
2612 # Original logger output is empty.
2613 self.assert_log_lines([])
2614
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002615 def test_config_9_ok(self):
2616 with captured_stdout() as output:
2617 self.apply_config(self.config9)
2618 logger = logging.getLogger("compiler.parser")
2619 #Nothing will be output since both handler and logger are set to WARNING
2620 logger.info(self.next_message())
2621 self.assert_log_lines([], stream=output)
2622 self.apply_config(self.config9a)
2623 #Nothing will be output since both handler is still set to WARNING
2624 logger.info(self.next_message())
2625 self.assert_log_lines([], stream=output)
2626 self.apply_config(self.config9b)
2627 #Message should now be output
2628 logger.info(self.next_message())
2629 self.assert_log_lines([
2630 ('INFO', '3'),
2631 ], stream=output)
2632
2633 def test_config_10_ok(self):
2634 with captured_stdout() as output:
2635 self.apply_config(self.config10)
2636 logger = logging.getLogger("compiler.parser")
2637 logger.warning(self.next_message())
2638 logger = logging.getLogger('compiler')
2639 #Not output, because filtered
2640 logger.warning(self.next_message())
2641 logger = logging.getLogger('compiler.lexer')
2642 #Not output, because filtered
2643 logger.warning(self.next_message())
2644 logger = logging.getLogger("compiler.parser.codegen")
2645 #Output, as not filtered
2646 logger.error(self.next_message())
2647 self.assert_log_lines([
2648 ('WARNING', '1'),
2649 ('ERROR', '4'),
2650 ], stream=output)
2651
2652 def test_config11_ok(self):
2653 self.test_config1_ok(self.config11)
2654
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002655 def test_config12_failure(self):
2656 self.assertRaises(Exception, self.apply_config, self.config12)
2657
2658 def test_config13_failure(self):
2659 self.assertRaises(Exception, self.apply_config, self.config13)
2660
Victor Stinner45df8202010-04-28 22:31:17 +00002661 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002662 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002663 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002664 # Ask for a randomly assigned port (by using port 0)
2665 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002666 t.start()
2667 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002668 # Now get the port allocated
2669 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002670 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002671 try:
2672 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2673 sock.settimeout(2.0)
2674 sock.connect(('localhost', port))
2675
2676 slen = struct.pack('>L', len(text))
2677 s = slen + text
2678 sentsofar = 0
2679 left = len(s)
2680 while left > 0:
2681 sent = sock.send(s[sentsofar:])
2682 sentsofar += sent
2683 left -= sent
2684 sock.close()
2685 finally:
2686 t.ready.wait(2.0)
2687 logging.config.stopListening()
2688 t.join(2.0)
2689
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002690 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002691 def test_listen_config_10_ok(self):
2692 with captured_stdout() as output:
2693 self.setup_via_listener(json.dumps(self.config10))
2694 logger = logging.getLogger("compiler.parser")
2695 logger.warning(self.next_message())
2696 logger = logging.getLogger('compiler')
2697 #Not output, because filtered
2698 logger.warning(self.next_message())
2699 logger = logging.getLogger('compiler.lexer')
2700 #Not output, because filtered
2701 logger.warning(self.next_message())
2702 logger = logging.getLogger("compiler.parser.codegen")
2703 #Output, as not filtered
2704 logger.error(self.next_message())
2705 self.assert_log_lines([
2706 ('WARNING', '1'),
2707 ('ERROR', '4'),
2708 ], stream=output)
2709
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002710 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002711 def test_listen_config_1_ok(self):
2712 with captured_stdout() as output:
2713 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2714 logger = logging.getLogger("compiler.parser")
2715 # Both will output a message
2716 logger.info(self.next_message())
2717 logger.error(self.next_message())
2718 self.assert_log_lines([
2719 ('INFO', '1'),
2720 ('ERROR', '2'),
2721 ], stream=output)
2722 # Original logger output is empty.
2723 self.assert_log_lines([])
2724
Vinay Sajip3f885b52013-03-22 15:19:54 +00002725 def test_out_of_order(self):
2726 self.apply_config(self.out_of_order)
2727 handler = logging.getLogger('mymodule').handlers[0]
2728 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00002729 self.assertIsInstance(handler.formatter._style,
2730 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002731
Vinay Sajip373baef2011-04-26 20:05:24 +01002732 def test_baseconfig(self):
2733 d = {
2734 'atuple': (1, 2, 3),
2735 'alist': ['a', 'b', 'c'],
2736 'adict': {'d': 'e', 'f': 3 },
2737 'nest1': ('g', ('h', 'i'), 'j'),
2738 'nest2': ['k', ['l', 'm'], 'n'],
2739 'nest3': ['o', 'cfg://alist', 'p'],
2740 }
2741 bc = logging.config.BaseConfigurator(d)
2742 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2743 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2744 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2745 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2746 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2747 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2748 v = bc.convert('cfg://nest3')
2749 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2750 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2751 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2752 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002753
2754class ManagerTest(BaseTest):
2755 def test_manager_loggerclass(self):
2756 logged = []
2757
2758 class MyLogger(logging.Logger):
2759 def _log(self, level, msg, args, exc_info=None, extra=None):
2760 logged.append(msg)
2761
2762 man = logging.Manager(None)
2763 self.assertRaises(TypeError, man.setLoggerClass, int)
2764 man.setLoggerClass(MyLogger)
2765 logger = man.getLogger('test')
2766 logger.warning('should appear in logged')
2767 logging.warning('should not appear in logged')
2768
2769 self.assertEqual(logged, ['should appear in logged'])
2770
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002771 def test_set_log_record_factory(self):
2772 man = logging.Manager(None)
2773 expected = object()
2774 man.setLogRecordFactory(expected)
2775 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002776
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002777class ChildLoggerTest(BaseTest):
2778 def test_child_loggers(self):
2779 r = logging.getLogger()
2780 l1 = logging.getLogger('abc')
2781 l2 = logging.getLogger('def.ghi')
2782 c1 = r.getChild('xyz')
2783 c2 = r.getChild('uvw.xyz')
2784 self.assertTrue(c1 is logging.getLogger('xyz'))
2785 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2786 c1 = l1.getChild('def')
2787 c2 = c1.getChild('ghi')
2788 c3 = l1.getChild('def.ghi')
2789 self.assertTrue(c1 is logging.getLogger('abc.def'))
2790 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2791 self.assertTrue(c2 is c3)
2792
2793
Vinay Sajip6fac8172010-10-19 20:44:14 +00002794class DerivedLogRecord(logging.LogRecord):
2795 pass
2796
Vinay Sajip61561522010-12-03 11:50:38 +00002797class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002798
2799 def setUp(self):
2800 class CheckingFilter(logging.Filter):
2801 def __init__(self, cls):
2802 self.cls = cls
2803
2804 def filter(self, record):
2805 t = type(record)
2806 if t is not self.cls:
2807 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2808 self.cls)
2809 raise TypeError(msg)
2810 return True
2811
2812 BaseTest.setUp(self)
2813 self.filter = CheckingFilter(DerivedLogRecord)
2814 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002815 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002816
2817 def tearDown(self):
2818 self.root_logger.removeFilter(self.filter)
2819 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002820 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002821
2822 def test_logrecord_class(self):
2823 self.assertRaises(TypeError, self.root_logger.warning,
2824 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002825 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002826 self.root_logger.error(self.next_message())
2827 self.assert_log_lines([
2828 ('root', 'ERROR', '2'),
2829 ])
2830
2831
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002832class QueueHandlerTest(BaseTest):
2833 # Do not bother with a logger name group.
2834 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2835
2836 def setUp(self):
2837 BaseTest.setUp(self)
2838 self.queue = queue.Queue(-1)
2839 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2840 self.que_logger = logging.getLogger('que')
2841 self.que_logger.propagate = False
2842 self.que_logger.setLevel(logging.WARNING)
2843 self.que_logger.addHandler(self.que_hdlr)
2844
2845 def tearDown(self):
2846 self.que_hdlr.close()
2847 BaseTest.tearDown(self)
2848
2849 def test_queue_handler(self):
2850 self.que_logger.debug(self.next_message())
2851 self.assertRaises(queue.Empty, self.queue.get_nowait)
2852 self.que_logger.info(self.next_message())
2853 self.assertRaises(queue.Empty, self.queue.get_nowait)
2854 msg = self.next_message()
2855 self.que_logger.warning(msg)
2856 data = self.queue.get_nowait()
2857 self.assertTrue(isinstance(data, logging.LogRecord))
2858 self.assertEqual(data.name, self.que_logger.name)
2859 self.assertEqual((data.msg, data.args), (msg, None))
2860
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002861 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2862 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002863 def test_queue_listener(self):
2864 handler = TestHandler(Matcher())
2865 listener = logging.handlers.QueueListener(self.queue, handler)
2866 listener.start()
2867 try:
2868 self.que_logger.warning(self.next_message())
2869 self.que_logger.error(self.next_message())
2870 self.que_logger.critical(self.next_message())
2871 finally:
2872 listener.stop()
2873 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2874 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2875 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2876
Vinay Sajip37eb3382011-04-26 20:26:41 +01002877ZERO = datetime.timedelta(0)
2878
2879class UTC(datetime.tzinfo):
2880 def utcoffset(self, dt):
2881 return ZERO
2882
2883 dst = utcoffset
2884
2885 def tzname(self, dt):
2886 return 'UTC'
2887
2888utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002889
Vinay Sajipa39c5712010-10-25 13:57:39 +00002890class FormatterTest(unittest.TestCase):
2891 def setUp(self):
2892 self.common = {
2893 'name': 'formatter.test',
2894 'level': logging.DEBUG,
2895 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2896 'lineno': 42,
2897 'exc_info': None,
2898 'func': None,
2899 'msg': 'Message with %d %s',
2900 'args': (2, 'placeholders'),
2901 }
2902 self.variants = {
2903 }
2904
2905 def get_record(self, name=None):
2906 result = dict(self.common)
2907 if name is not None:
2908 result.update(self.variants[name])
2909 return logging.makeLogRecord(result)
2910
2911 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002912 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002913 r = self.get_record()
2914 f = logging.Formatter('${%(message)s}')
2915 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2916 f = logging.Formatter('%(random)s')
2917 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002918 self.assertFalse(f.usesTime())
2919 f = logging.Formatter('%(asctime)s')
2920 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002921 f = logging.Formatter('%(asctime)-15s')
2922 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002923 f = logging.Formatter('asctime')
2924 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002925
2926 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002927 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002928 r = self.get_record()
2929 f = logging.Formatter('$%{message}%$', style='{')
2930 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2931 f = logging.Formatter('{random}', style='{')
2932 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002933 self.assertFalse(f.usesTime())
2934 f = logging.Formatter('{asctime}', style='{')
2935 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002936 f = logging.Formatter('{asctime!s:15}', style='{')
2937 self.assertTrue(f.usesTime())
2938 f = logging.Formatter('{asctime:15}', style='{')
2939 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002940 f = logging.Formatter('asctime', style='{')
2941 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002942
2943 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002944 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002945 r = self.get_record()
2946 f = logging.Formatter('$message', style='$')
2947 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2948 f = logging.Formatter('$$%${message}%$$', style='$')
2949 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2950 f = logging.Formatter('${random}', style='$')
2951 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002952 self.assertFalse(f.usesTime())
2953 f = logging.Formatter('${asctime}', style='$')
2954 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002955 f = logging.Formatter('${asctime', style='$')
2956 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002957 f = logging.Formatter('$asctime', style='$')
2958 self.assertTrue(f.usesTime())
2959 f = logging.Formatter('asctime', style='$')
2960 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002961
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002962 def test_invalid_style(self):
2963 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2964
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002965 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002966 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01002967 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
2968 # We use None to indicate we want the local timezone
2969 # We're essentially converting a UTC time to local time
2970 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002971 r.msecs = 123
2972 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002973 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002974 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2975 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002976 f.format(r)
2977 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2978
2979class TestBufferingFormatter(logging.BufferingFormatter):
2980 def formatHeader(self, records):
2981 return '[(%d)' % len(records)
2982
2983 def formatFooter(self, records):
2984 return '(%d)]' % len(records)
2985
2986class BufferingFormatterTest(unittest.TestCase):
2987 def setUp(self):
2988 self.records = [
2989 logging.makeLogRecord({'msg': 'one'}),
2990 logging.makeLogRecord({'msg': 'two'}),
2991 ]
2992
2993 def test_default(self):
2994 f = logging.BufferingFormatter()
2995 self.assertEqual('', f.format([]))
2996 self.assertEqual('onetwo', f.format(self.records))
2997
2998 def test_custom(self):
2999 f = TestBufferingFormatter()
3000 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
3001 lf = logging.Formatter('<%(message)s>')
3002 f = TestBufferingFormatter(lf)
3003 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003004
3005class ExceptionTest(BaseTest):
3006 def test_formatting(self):
3007 r = self.root_logger
3008 h = RecordingHandler()
3009 r.addHandler(h)
3010 try:
3011 raise RuntimeError('deliberate mistake')
3012 except:
3013 logging.exception('failed', stack_info=True)
3014 r.removeHandler(h)
3015 h.close()
3016 r = h.records[0]
3017 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
3018 'call last):\n'))
3019 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
3020 'deliberate mistake'))
3021 self.assertTrue(r.stack_info.startswith('Stack (most recent '
3022 'call last):\n'))
3023 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
3024 'stack_info=True)'))
3025
3026
Vinay Sajip5a27d402010-12-10 11:42:57 +00003027class LastResortTest(BaseTest):
3028 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003029 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00003030 root = self.root_logger
3031 root.removeHandler(self.root_hdlr)
3032 old_stderr = sys.stderr
3033 old_lastresort = logging.lastResort
3034 old_raise_exceptions = logging.raiseExceptions
3035 try:
3036 sys.stderr = sio = io.StringIO()
Vinay Sajip8188f582011-07-25 19:58:13 +01003037 root.debug('This should not appear')
3038 self.assertEqual(sio.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00003039 root.warning('This is your final chance!')
3040 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
3041 #No handlers and no last resort, so 'No handlers' message
3042 logging.lastResort = None
3043 sys.stderr = sio = io.StringIO()
3044 root.warning('This is your final chance!')
3045 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
3046 # 'No handlers' message only printed once
3047 sys.stderr = sio = io.StringIO()
3048 root.warning('This is your final chance!')
3049 self.assertEqual(sio.getvalue(), '')
3050 root.manager.emittedNoHandlerWarning = False
3051 #If raiseExceptions is False, no message is printed
3052 logging.raiseExceptions = False
3053 sys.stderr = sio = io.StringIO()
3054 root.warning('This is your final chance!')
3055 self.assertEqual(sio.getvalue(), '')
3056 finally:
3057 sys.stderr = old_stderr
3058 root.addHandler(self.root_hdlr)
3059 logging.lastResort = old_lastresort
3060 logging.raiseExceptions = old_raise_exceptions
3061
3062
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003063class FakeHandler:
3064
3065 def __init__(self, identifier, called):
3066 for method in ('acquire', 'flush', 'close', 'release'):
3067 setattr(self, method, self.record_call(identifier, method, called))
3068
3069 def record_call(self, identifier, method_name, called):
3070 def inner():
3071 called.append('{} - {}'.format(identifier, method_name))
3072 return inner
3073
3074
3075class RecordingHandler(logging.NullHandler):
3076
3077 def __init__(self, *args, **kwargs):
3078 super(RecordingHandler, self).__init__(*args, **kwargs)
3079 self.records = []
3080
3081 def handle(self, record):
3082 """Keep track of all the emitted records."""
3083 self.records.append(record)
3084
3085
3086class ShutdownTest(BaseTest):
3087
Vinay Sajip5e66b162011-04-20 15:41:14 +01003088 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003089
3090 def setUp(self):
3091 super(ShutdownTest, self).setUp()
3092 self.called = []
3093
3094 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003095 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003096
3097 def raise_error(self, error):
3098 def inner():
3099 raise error()
3100 return inner
3101
3102 def test_no_failure(self):
3103 # create some fake handlers
3104 handler0 = FakeHandler(0, self.called)
3105 handler1 = FakeHandler(1, self.called)
3106 handler2 = FakeHandler(2, self.called)
3107
3108 # create live weakref to those handlers
3109 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
3110
3111 logging.shutdown(handlerList=list(handlers))
3112
3113 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
3114 '1 - acquire', '1 - flush', '1 - close', '1 - release',
3115 '0 - acquire', '0 - flush', '0 - close', '0 - release']
3116 self.assertEqual(expected, self.called)
3117
3118 def _test_with_failure_in_method(self, method, error):
3119 handler = FakeHandler(0, self.called)
3120 setattr(handler, method, self.raise_error(error))
3121 handlers = [logging.weakref.ref(handler)]
3122
3123 logging.shutdown(handlerList=list(handlers))
3124
3125 self.assertEqual('0 - release', self.called[-1])
3126
3127 def test_with_ioerror_in_acquire(self):
3128 self._test_with_failure_in_method('acquire', IOError)
3129
3130 def test_with_ioerror_in_flush(self):
3131 self._test_with_failure_in_method('flush', IOError)
3132
3133 def test_with_ioerror_in_close(self):
3134 self._test_with_failure_in_method('close', IOError)
3135
3136 def test_with_valueerror_in_acquire(self):
3137 self._test_with_failure_in_method('acquire', ValueError)
3138
3139 def test_with_valueerror_in_flush(self):
3140 self._test_with_failure_in_method('flush', ValueError)
3141
3142 def test_with_valueerror_in_close(self):
3143 self._test_with_failure_in_method('close', ValueError)
3144
3145 def test_with_other_error_in_acquire_without_raise(self):
3146 logging.raiseExceptions = False
3147 self._test_with_failure_in_method('acquire', IndexError)
3148
3149 def test_with_other_error_in_flush_without_raise(self):
3150 logging.raiseExceptions = False
3151 self._test_with_failure_in_method('flush', IndexError)
3152
3153 def test_with_other_error_in_close_without_raise(self):
3154 logging.raiseExceptions = False
3155 self._test_with_failure_in_method('close', IndexError)
3156
3157 def test_with_other_error_in_acquire_with_raise(self):
3158 logging.raiseExceptions = True
3159 self.assertRaises(IndexError, self._test_with_failure_in_method,
3160 'acquire', IndexError)
3161
3162 def test_with_other_error_in_flush_with_raise(self):
3163 logging.raiseExceptions = True
3164 self.assertRaises(IndexError, self._test_with_failure_in_method,
3165 'flush', IndexError)
3166
3167 def test_with_other_error_in_close_with_raise(self):
3168 logging.raiseExceptions = True
3169 self.assertRaises(IndexError, self._test_with_failure_in_method,
3170 'close', IndexError)
3171
3172
3173class ModuleLevelMiscTest(BaseTest):
3174
Vinay Sajip5e66b162011-04-20 15:41:14 +01003175 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003176
3177 def test_disable(self):
3178 old_disable = logging.root.manager.disable
3179 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003180 self.assertEqual(old_disable, 0)
3181 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003182
3183 logging.disable(83)
3184 self.assertEqual(logging.root.manager.disable, 83)
3185
3186 def _test_log(self, method, level=None):
3187 called = []
3188 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003189 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003190
3191 recording = RecordingHandler()
3192 logging.root.addHandler(recording)
3193
3194 log_method = getattr(logging, method)
3195 if level is not None:
3196 log_method(level, "test me: %r", recording)
3197 else:
3198 log_method("test me: %r", recording)
3199
3200 self.assertEqual(len(recording.records), 1)
3201 record = recording.records[0]
3202 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3203
3204 expected_level = level if level is not None else getattr(logging, method.upper())
3205 self.assertEqual(record.levelno, expected_level)
3206
3207 # basicConfig was not called!
3208 self.assertEqual(called, [])
3209
3210 def test_log(self):
3211 self._test_log('log', logging.ERROR)
3212
3213 def test_debug(self):
3214 self._test_log('debug')
3215
3216 def test_info(self):
3217 self._test_log('info')
3218
3219 def test_warning(self):
3220 self._test_log('warning')
3221
3222 def test_error(self):
3223 self._test_log('error')
3224
3225 def test_critical(self):
3226 self._test_log('critical')
3227
3228 def test_set_logger_class(self):
3229 self.assertRaises(TypeError, logging.setLoggerClass, object)
3230
3231 class MyLogger(logging.Logger):
3232 pass
3233
3234 logging.setLoggerClass(MyLogger)
3235 self.assertEqual(logging.getLoggerClass(), MyLogger)
3236
3237 logging.setLoggerClass(logging.Logger)
3238 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3239
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003240class LogRecordTest(BaseTest):
3241 def test_str_rep(self):
3242 r = logging.makeLogRecord({})
3243 s = str(r)
3244 self.assertTrue(s.startswith('<LogRecord: '))
3245 self.assertTrue(s.endswith('>'))
3246
3247 def test_dict_arg(self):
3248 h = RecordingHandler()
3249 r = logging.getLogger()
3250 r.addHandler(h)
3251 d = {'less' : 'more' }
3252 logging.warning('less is %(less)s', d)
3253 self.assertIs(h.records[0].args, d)
3254 self.assertEqual(h.records[0].message, 'less is more')
3255 r.removeHandler(h)
3256 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003257
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003258 def test_multiprocessing(self):
3259 r = logging.makeLogRecord({})
3260 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003261 try:
3262 import multiprocessing as mp
3263 r = logging.makeLogRecord({})
3264 self.assertEqual(r.processName, mp.current_process().name)
3265 except ImportError:
3266 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003267
3268 def test_optional(self):
3269 r = logging.makeLogRecord({})
3270 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003271 if threading:
3272 NOT_NONE(r.thread)
3273 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003274 NOT_NONE(r.process)
3275 NOT_NONE(r.processName)
3276 log_threads = logging.logThreads
3277 log_processes = logging.logProcesses
3278 log_multiprocessing = logging.logMultiprocessing
3279 try:
3280 logging.logThreads = False
3281 logging.logProcesses = False
3282 logging.logMultiprocessing = False
3283 r = logging.makeLogRecord({})
3284 NONE = self.assertIsNone
3285 NONE(r.thread)
3286 NONE(r.threadName)
3287 NONE(r.process)
3288 NONE(r.processName)
3289 finally:
3290 logging.logThreads = log_threads
3291 logging.logProcesses = log_processes
3292 logging.logMultiprocessing = log_multiprocessing
3293
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003294class BasicConfigTest(unittest.TestCase):
3295
Vinay Sajip95bf5042011-04-20 11:50:56 +01003296 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003297
3298 def setUp(self):
3299 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003300 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003301 self.saved_handlers = logging._handlers.copy()
3302 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003303 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003304 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003305 logging.root.handlers = []
3306
3307 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003308 for h in logging.root.handlers[:]:
3309 logging.root.removeHandler(h)
3310 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003311 super(BasicConfigTest, self).tearDown()
3312
Vinay Sajip3def7e02011-04-20 10:58:06 +01003313 def cleanup(self):
3314 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003315 logging._handlers.clear()
3316 logging._handlers.update(self.saved_handlers)
3317 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003318 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003319
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003320 def test_no_kwargs(self):
3321 logging.basicConfig()
3322
3323 # handler defaults to a StreamHandler to sys.stderr
3324 self.assertEqual(len(logging.root.handlers), 1)
3325 handler = logging.root.handlers[0]
3326 self.assertIsInstance(handler, logging.StreamHandler)
3327 self.assertEqual(handler.stream, sys.stderr)
3328
3329 formatter = handler.formatter
3330 # format defaults to logging.BASIC_FORMAT
3331 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3332 # datefmt defaults to None
3333 self.assertIsNone(formatter.datefmt)
3334 # style defaults to %
3335 self.assertIsInstance(formatter._style, logging.PercentStyle)
3336
Florent Xiclunae41f0de2011-11-11 19:39:25 +01003337 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003338 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003339
3340 def test_filename(self):
3341 logging.basicConfig(filename='test.log')
3342
3343 self.assertEqual(len(logging.root.handlers), 1)
3344 handler = logging.root.handlers[0]
3345 self.assertIsInstance(handler, logging.FileHandler)
3346
3347 expected = logging.FileHandler('test.log', 'a')
3348 self.addCleanup(expected.close)
3349 self.assertEqual(handler.stream.mode, expected.stream.mode)
3350 self.assertEqual(handler.stream.name, expected.stream.name)
3351
3352 def test_filemode(self):
3353 logging.basicConfig(filename='test.log', filemode='wb')
3354
3355 handler = logging.root.handlers[0]
3356 expected = logging.FileHandler('test.log', 'wb')
3357 self.addCleanup(expected.close)
3358 self.assertEqual(handler.stream.mode, expected.stream.mode)
3359
3360 def test_stream(self):
3361 stream = io.StringIO()
3362 self.addCleanup(stream.close)
3363 logging.basicConfig(stream=stream)
3364
3365 self.assertEqual(len(logging.root.handlers), 1)
3366 handler = logging.root.handlers[0]
3367 self.assertIsInstance(handler, logging.StreamHandler)
3368 self.assertEqual(handler.stream, stream)
3369
3370 def test_format(self):
3371 logging.basicConfig(format='foo')
3372
3373 formatter = logging.root.handlers[0].formatter
3374 self.assertEqual(formatter._style._fmt, 'foo')
3375
3376 def test_datefmt(self):
3377 logging.basicConfig(datefmt='bar')
3378
3379 formatter = logging.root.handlers[0].formatter
3380 self.assertEqual(formatter.datefmt, 'bar')
3381
3382 def test_style(self):
3383 logging.basicConfig(style='$')
3384
3385 formatter = logging.root.handlers[0].formatter
3386 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3387
3388 def test_level(self):
3389 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003390 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003391
3392 logging.basicConfig(level=57)
3393 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003394 # Test that second call has no effect
3395 logging.basicConfig(level=58)
3396 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003397
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003398 def test_incompatible(self):
3399 assertRaises = self.assertRaises
3400 handlers = [logging.StreamHandler()]
3401 stream = sys.stderr
3402 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3403 stream=stream)
3404 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3405 handlers=handlers)
3406 assertRaises(ValueError, logging.basicConfig, stream=stream,
3407 handlers=handlers)
3408
3409 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003410 handlers = [
3411 logging.StreamHandler(),
3412 logging.StreamHandler(sys.stdout),
3413 logging.StreamHandler(),
3414 ]
3415 f = logging.Formatter()
3416 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003417 logging.basicConfig(handlers=handlers)
3418 self.assertIs(handlers[0], logging.root.handlers[0])
3419 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003420 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003421 self.assertIsNotNone(handlers[0].formatter)
3422 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003423 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003424 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3425
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003426 def _test_log(self, method, level=None):
3427 # logging.root has no handlers so basicConfig should be called
3428 called = []
3429
3430 old_basic_config = logging.basicConfig
3431 def my_basic_config(*a, **kw):
3432 old_basic_config()
3433 old_level = logging.root.level
3434 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003435 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003436 called.append((a, kw))
3437
3438 patch(self, logging, 'basicConfig', my_basic_config)
3439
3440 log_method = getattr(logging, method)
3441 if level is not None:
3442 log_method(level, "test me")
3443 else:
3444 log_method("test me")
3445
3446 # basicConfig was called with no arguments
3447 self.assertEqual(called, [((), {})])
3448
3449 def test_log(self):
3450 self._test_log('log', logging.WARNING)
3451
3452 def test_debug(self):
3453 self._test_log('debug')
3454
3455 def test_info(self):
3456 self._test_log('info')
3457
3458 def test_warning(self):
3459 self._test_log('warning')
3460
3461 def test_error(self):
3462 self._test_log('error')
3463
3464 def test_critical(self):
3465 self._test_log('critical')
3466
3467
3468class LoggerAdapterTest(unittest.TestCase):
3469
3470 def setUp(self):
3471 super(LoggerAdapterTest, self).setUp()
3472 old_handler_list = logging._handlerList[:]
3473
3474 self.recording = RecordingHandler()
3475 self.logger = logging.root
3476 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003477 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003478 self.addCleanup(self.recording.close)
3479
3480 def cleanup():
3481 logging._handlerList[:] = old_handler_list
3482
3483 self.addCleanup(cleanup)
3484 self.addCleanup(logging.shutdown)
3485 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3486
3487 def test_exception(self):
3488 msg = 'testing exception: %r'
3489 exc = None
3490 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003491 1 / 0
3492 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003493 exc = e
3494 self.adapter.exception(msg, self.recording)
3495
3496 self.assertEqual(len(self.recording.records), 1)
3497 record = self.recording.records[0]
3498 self.assertEqual(record.levelno, logging.ERROR)
3499 self.assertEqual(record.msg, msg)
3500 self.assertEqual(record.args, (self.recording,))
3501 self.assertEqual(record.exc_info,
3502 (exc.__class__, exc, exc.__traceback__))
3503
3504 def test_critical(self):
3505 msg = 'critical test! %r'
3506 self.adapter.critical(msg, self.recording)
3507
3508 self.assertEqual(len(self.recording.records), 1)
3509 record = self.recording.records[0]
3510 self.assertEqual(record.levelno, logging.CRITICAL)
3511 self.assertEqual(record.msg, msg)
3512 self.assertEqual(record.args, (self.recording,))
3513
3514 def test_is_enabled_for(self):
3515 old_disable = self.adapter.logger.manager.disable
3516 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003517 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3518 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003519 self.assertFalse(self.adapter.isEnabledFor(32))
3520
3521 def test_has_handlers(self):
3522 self.assertTrue(self.adapter.hasHandlers())
3523
3524 for handler in self.logger.handlers:
3525 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003526
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003527 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003528 self.assertFalse(self.adapter.hasHandlers())
3529
3530
3531class LoggerTest(BaseTest):
3532
3533 def setUp(self):
3534 super(LoggerTest, self).setUp()
3535 self.recording = RecordingHandler()
3536 self.logger = logging.Logger(name='blah')
3537 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003538 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003539 self.addCleanup(self.recording.close)
3540 self.addCleanup(logging.shutdown)
3541
3542 def test_set_invalid_level(self):
3543 self.assertRaises(TypeError, self.logger.setLevel, object())
3544
3545 def test_exception(self):
3546 msg = 'testing exception: %r'
3547 exc = None
3548 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003549 1 / 0
3550 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003551 exc = e
3552 self.logger.exception(msg, self.recording)
3553
3554 self.assertEqual(len(self.recording.records), 1)
3555 record = self.recording.records[0]
3556 self.assertEqual(record.levelno, logging.ERROR)
3557 self.assertEqual(record.msg, msg)
3558 self.assertEqual(record.args, (self.recording,))
3559 self.assertEqual(record.exc_info,
3560 (exc.__class__, exc, exc.__traceback__))
3561
3562 def test_log_invalid_level_with_raise(self):
3563 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003564 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003565
3566 logging.raiseExceptions = True
3567 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3568
3569 def test_log_invalid_level_no_raise(self):
3570 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003571 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003572
3573 logging.raiseExceptions = False
3574 self.logger.log('10', 'test message') # no exception happens
3575
3576 def test_find_caller_with_stack_info(self):
3577 called = []
3578 patch(self, logging.traceback, 'print_stack',
3579 lambda f, file: called.append(file.getvalue()))
3580
3581 self.logger.findCaller(stack_info=True)
3582
3583 self.assertEqual(len(called), 1)
3584 self.assertEqual('Stack (most recent call last):\n', called[0])
3585
3586 def test_make_record_with_extra_overwrite(self):
3587 name = 'my record'
3588 level = 13
3589 fn = lno = msg = args = exc_info = func = sinfo = None
3590 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3591 exc_info, func, sinfo)
3592
3593 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3594 extra = {key: 'some value'}
3595 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3596 fn, lno, msg, args, exc_info,
3597 extra=extra, sinfo=sinfo)
3598
3599 def test_make_record_with_extra_no_overwrite(self):
3600 name = 'my record'
3601 level = 13
3602 fn = lno = msg = args = exc_info = func = sinfo = None
3603 extra = {'valid_key': 'some value'}
3604 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3605 exc_info, extra=extra, sinfo=sinfo)
3606 self.assertIn('valid_key', result.__dict__)
3607
3608 def test_has_handlers(self):
3609 self.assertTrue(self.logger.hasHandlers())
3610
3611 for handler in self.logger.handlers:
3612 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003613 self.assertFalse(self.logger.hasHandlers())
3614
3615 def test_has_handlers_no_propagate(self):
3616 child_logger = logging.getLogger('blah.child')
3617 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003618 self.assertFalse(child_logger.hasHandlers())
3619
3620 def test_is_enabled_for(self):
3621 old_disable = self.logger.manager.disable
3622 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003623 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003624 self.assertFalse(self.logger.isEnabledFor(22))
3625
Florent Xicluna5252f9f2011-11-07 19:43:05 +01003626 def test_root_logger_aliases(self):
3627 root = logging.getLogger()
3628 self.assertIs(root, logging.root)
3629 self.assertIs(root, logging.getLogger(None))
3630 self.assertIs(root, logging.getLogger(''))
3631 self.assertIs(root, logging.getLogger('foo').root)
3632 self.assertIs(root, logging.getLogger('foo.bar').root)
3633 self.assertIs(root, logging.getLogger('foo').parent)
3634
3635 self.assertIsNot(root, logging.getLogger('\0'))
3636 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
3637
3638 def test_invalid_names(self):
3639 self.assertRaises(TypeError, logging.getLogger, any)
3640 self.assertRaises(TypeError, logging.getLogger, b'foo')
3641
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003642
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003643class BaseFileTest(BaseTest):
3644 "Base class for handler tests that write log files"
3645
3646 def setUp(self):
3647 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003648 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3649 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003650 self.rmfiles = []
3651
3652 def tearDown(self):
3653 for fn in self.rmfiles:
3654 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003655 if os.path.exists(self.fn):
3656 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003657 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003658
3659 def assertLogFile(self, filename):
3660 "Assert a log file is there and register it for deletion"
3661 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003662 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003663 self.rmfiles.append(filename)
3664
3665
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003666class FileHandlerTest(BaseFileTest):
3667 def test_delay(self):
3668 os.unlink(self.fn)
3669 fh = logging.FileHandler(self.fn, delay=True)
3670 self.assertIsNone(fh.stream)
3671 self.assertFalse(os.path.exists(self.fn))
3672 fh.handle(logging.makeLogRecord({}))
3673 self.assertIsNotNone(fh.stream)
3674 self.assertTrue(os.path.exists(self.fn))
3675 fh.close()
3676
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003677class RotatingFileHandlerTest(BaseFileTest):
3678 def next_rec(self):
3679 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3680 self.next_message(), None, None, None)
3681
3682 def test_should_not_rollover(self):
3683 # If maxbytes is zero rollover never occurs
3684 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3685 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003686 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003687
3688 def test_should_rollover(self):
3689 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3690 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003691 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003692
3693 def test_file_created(self):
3694 # checks that the file is created and assumes it was created
3695 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003696 rh = logging.handlers.RotatingFileHandler(self.fn)
3697 rh.emit(self.next_rec())
3698 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003699 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003700
3701 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00003702 def namer(name):
3703 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003704 rh = logging.handlers.RotatingFileHandler(
3705 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003706 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003707 rh.emit(self.next_rec())
3708 self.assertLogFile(self.fn)
3709 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003710 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003711 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003712 self.assertLogFile(namer(self.fn + ".2"))
3713 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
3714 rh.close()
3715
Eric V. Smith851cad72012-03-11 22:46:04 -07003716 @requires_zlib
Vinay Sajip23b94d02012-01-04 12:02:26 +00003717 def test_rotator(self):
3718 def namer(name):
3719 return name + ".gz"
3720
3721 def rotator(source, dest):
3722 with open(source, "rb") as sf:
3723 data = sf.read()
3724 compressed = zlib.compress(data, 9)
3725 with open(dest, "wb") as df:
3726 df.write(compressed)
3727 os.remove(source)
3728
3729 rh = logging.handlers.RotatingFileHandler(
3730 self.fn, backupCount=2, maxBytes=1)
3731 rh.rotator = rotator
3732 rh.namer = namer
3733 m1 = self.next_rec()
3734 rh.emit(m1)
3735 self.assertLogFile(self.fn)
3736 m2 = self.next_rec()
3737 rh.emit(m2)
3738 fn = namer(self.fn + ".1")
3739 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003740 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00003741 with open(fn, "rb") as f:
3742 compressed = f.read()
3743 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003744 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003745 rh.emit(self.next_rec())
3746 fn = namer(self.fn + ".2")
3747 self.assertLogFile(fn)
3748 with open(fn, "rb") as f:
3749 compressed = f.read()
3750 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003751 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003752 rh.emit(self.next_rec())
3753 fn = namer(self.fn + ".2")
3754 with open(fn, "rb") as f:
3755 compressed = f.read()
3756 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003757 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003758 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00003759 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003760
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003761class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003762 # other test methods added below
3763 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003764 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3765 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00003766 fmt = logging.Formatter('%(asctime)s %(message)s')
3767 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003768 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003769 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003770 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003771 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00003772 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003773 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01003774 fh.close()
3775 # At this point, we should have a recent rotated file which we
3776 # can test for the existence of. However, in practice, on some
3777 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003778 # in time to go to look for the log file. So, we go back a fair
3779 # bit, and stop as soon as we see a rotated file. In theory this
3780 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003781 found = False
3782 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003783 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003784 for secs in range(GO_BACK):
3785 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003786 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3787 found = os.path.exists(fn)
3788 if found:
3789 self.rmfiles.append(fn)
3790 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003791 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3792 if not found:
3793 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003794 dn, fn = os.path.split(self.fn)
3795 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003796 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3797 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00003798 for f in files:
3799 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00003800 path = os.path.join(dn, f)
3801 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00003802 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01003803 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003804
Vinay Sajip0372e102011-05-05 12:59:14 +01003805 def test_invalid(self):
3806 assertRaises = self.assertRaises
3807 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003808 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003809 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003810 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003811 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003812 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003813
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003814def secs(**kw):
3815 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3816
3817for when, exp in (('S', 1),
3818 ('M', 60),
3819 ('H', 60 * 60),
3820 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003821 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003822 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003823 ('W0', secs(days=4, hours=24)),
3824 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003825 def test_compute_rollover(self, when=when, exp=exp):
3826 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003827 self.fn, when=when, interval=1, backupCount=0, utc=True)
3828 currentTime = 0.0
3829 actual = rh.computeRollover(currentTime)
3830 if exp != actual:
3831 # Failures occur on some systems for MIDNIGHT and W0.
3832 # Print detailed calculation for MIDNIGHT so we can try to see
3833 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00003834 if when == 'MIDNIGHT':
3835 try:
3836 if rh.utc:
3837 t = time.gmtime(currentTime)
3838 else:
3839 t = time.localtime(currentTime)
3840 currentHour = t[3]
3841 currentMinute = t[4]
3842 currentSecond = t[5]
3843 # r is the number of seconds left between now and midnight
3844 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3845 currentMinute) * 60 +
3846 currentSecond)
3847 result = currentTime + r
3848 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3849 print('currentHour: %s' % currentHour, file=sys.stderr)
3850 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3851 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3852 print('r: %s' % r, file=sys.stderr)
3853 print('result: %s' % result, file=sys.stderr)
3854 except Exception:
3855 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3856 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003857 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003858 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3859
Vinay Sajip60ccd822011-05-09 17:32:09 +01003860
3861@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3862class NTEventLogHandlerTest(BaseTest):
3863 def test_basic(self):
3864 logtype = 'Application'
3865 elh = win32evtlog.OpenEventLog(None, logtype)
3866 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3867 h = logging.handlers.NTEventLogHandler('test_logging')
3868 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3869 h.handle(r)
3870 h.close()
3871 # Now see if the event is recorded
3872 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3873 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3874 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3875 found = False
3876 GO_BACK = 100
3877 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3878 for e in events:
3879 if e.SourceName != 'test_logging':
3880 continue
3881 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3882 if msg != 'Test Log Message\r\n':
3883 continue
3884 found = True
3885 break
3886 msg = 'Record not found in event log, went back %d records' % GO_BACK
3887 self.assertTrue(found, msg=msg)
3888
Christian Heimes180510d2008-03-03 19:15:45 +00003889# Set the locale to the platform-dependent default. I have no idea
3890# why the test does this, but in any case we save the current locale
3891# first and restore it at the end.
3892@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003893def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003894 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003895 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003896 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3897 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3898 ManagerTest, FormatterTest, BufferingFormatterTest,
3899 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3900 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3901 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3902 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003903 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003904 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003905 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003906 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003907
Christian Heimes180510d2008-03-03 19:15:45 +00003908if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003909 test_main()