blob: cb908fb4600b95ae8191fb64dfbbe2badff3c8f9 [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajip66b8b082012-04-24 23:25:30 +01003# Copyright 2001-2012 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00004#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19"""Test harness for the logging module. Run all tests.
20
Vinay Sajip66b8b082012-04-24 23:25:30 +010021Copyright (C) 2001-2012 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Christian Heimes180510d2008-03-03 19:15:45 +000024import logging
25import logging.handlers
26import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000027
Benjamin Petersonf91df042009-02-13 02:50:59 +000028import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Christian Heimes180510d2008-03-03 19:15:45 +000030import pickle
31import io
32import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000033import json
Christian Heimes180510d2008-03-03 19:15:45 +000034import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000035import queue
Vinay Sajip66b8b082012-04-24 23:25:30 +010036import random
Christian Heimes180510d2008-03-03 19:15:45 +000037import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000038import select
Christian Heimes180510d2008-03-03 19:15:45 +000039import socket
Christian Heimes180510d2008-03-03 19:15:45 +000040import struct
41import sys
42import tempfile
Eric V. Smith851cad72012-03-11 22:46:04 -070043from test.support import (captured_stdout, run_with_locale, run_unittest,
44 patch, requires_zlib, TestHandler, Matcher)
Christian Heimes180510d2008-03-03 19:15:45 +000045import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010046import time
Christian Heimes180510d2008-03-03 19:15:45 +000047import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000048import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000049import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000050try:
51 import threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010052 # The following imports are needed only for tests which
Florent Xicluna5252f9f2011-11-07 19:43:05 +010053 # require threading
Vinay Sajipce7c9782011-05-17 07:15:53 +010054 import asynchat
55 import asyncore
56 import errno
57 from http.server import HTTPServer, BaseHTTPRequestHandler
58 import smtpd
59 from urllib.parse import urlparse, parse_qs
60 from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
61 ThreadingTCPServer, StreamRequestHandler)
Victor Stinner45df8202010-04-28 22:31:17 +000062except ImportError:
63 threading = None
Vinay Sajip60ccd822011-05-09 17:32:09 +010064try:
65 import win32evtlog
66except ImportError:
67 win32evtlog = None
68try:
69 import win32evtlogutil
70except ImportError:
71 win32evtlogutil = None
72 win32evtlog = None
Eric V. Smith851cad72012-03-11 22:46:04 -070073try:
74 import zlib
75except ImportError:
76 pass
Christian Heimes18c66892008-02-17 13:31:39 +000077
78
Christian Heimes180510d2008-03-03 19:15:45 +000079class BaseTest(unittest.TestCase):
80
81 """Base class for logging tests."""
82
83 log_format = "%(name)s -> %(levelname)s: %(message)s"
84 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
85 message_num = 0
86
87 def setUp(self):
88 """Setup the default logging stream to an internal StringIO instance,
89 so that we can examine log output as we want."""
90 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000091 logging._acquireLock()
92 try:
Christian Heimes180510d2008-03-03 19:15:45 +000093 self.saved_handlers = logging._handlers.copy()
94 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000095 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000096 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000097 self.logger_states = logger_states = {}
98 for name in saved_loggers:
99 logger_states[name] = getattr(saved_loggers[name],
100 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +0000101 finally:
102 logging._releaseLock()
103
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100104 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +0000105 self.logger1 = logging.getLogger("\xab\xd7\xbb")
106 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000107
Christian Heimes180510d2008-03-03 19:15:45 +0000108 self.root_logger = logging.getLogger("")
109 self.original_logging_level = self.root_logger.getEffectiveLevel()
110
111 self.stream = io.StringIO()
112 self.root_logger.setLevel(logging.DEBUG)
113 self.root_hdlr = logging.StreamHandler(self.stream)
114 self.root_formatter = logging.Formatter(self.log_format)
115 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000116 if self.logger1.hasHandlers():
117 hlist = self.logger1.handlers + self.root_logger.handlers
118 raise AssertionError('Unexpected handlers: %s' % hlist)
119 if self.logger2.hasHandlers():
120 hlist = self.logger2.handlers + self.root_logger.handlers
121 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000122 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000123 self.assertTrue(self.logger1.hasHandlers())
124 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000125
126 def tearDown(self):
127 """Remove our logging stream, and restore the original logging
128 level."""
129 self.stream.close()
130 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000131 while self.root_logger.handlers:
132 h = self.root_logger.handlers[0]
133 self.root_logger.removeHandler(h)
134 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000135 self.root_logger.setLevel(self.original_logging_level)
136 logging._acquireLock()
137 try:
138 logging._levelNames.clear()
139 logging._levelNames.update(self.saved_level_names)
140 logging._handlers.clear()
141 logging._handlers.update(self.saved_handlers)
142 logging._handlerList[:] = self.saved_handler_list
143 loggerDict = logging.getLogger().manager.loggerDict
144 loggerDict.clear()
145 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000146 logger_states = self.logger_states
147 for name in self.logger_states:
148 if logger_states[name] is not None:
149 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000150 finally:
151 logging._releaseLock()
152
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000153 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000154 """Match the collected log lines against the regular expression
155 self.expected_log_pat, and compare the extracted group values to
156 the expected_values list of tuples."""
157 stream = stream or self.stream
158 pat = re.compile(self.expected_log_pat)
159 try:
160 stream.reset()
161 actual_lines = stream.readlines()
162 except AttributeError:
163 # StringIO.StringIO lacks a reset() method.
164 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100165 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000166 for actual, expected in zip(actual_lines, expected_values):
167 match = pat.search(actual)
168 if not match:
169 self.fail("Log line does not match expected pattern:\n" +
170 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000171 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000172 s = stream.read()
173 if s:
174 self.fail("Remaining output at end of log stream:\n" + s)
175
176 def next_message(self):
177 """Generate a message consisting solely of an auto-incrementing
178 integer."""
179 self.message_num += 1
180 return "%d" % self.message_num
181
182
183class BuiltinLevelsTest(BaseTest):
184 """Test builtin levels and their inheritance."""
185
186 def test_flat(self):
187 #Logging levels in a flat logger namespace.
188 m = self.next_message
189
190 ERR = logging.getLogger("ERR")
191 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000192 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000193 INF.setLevel(logging.INFO)
194 DEB = logging.getLogger("DEB")
195 DEB.setLevel(logging.DEBUG)
196
197 # These should log.
198 ERR.log(logging.CRITICAL, m())
199 ERR.error(m())
200
201 INF.log(logging.CRITICAL, m())
202 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100203 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000204 INF.info(m())
205
206 DEB.log(logging.CRITICAL, m())
207 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100208 DEB.warning(m())
209 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000210 DEB.debug(m())
211
212 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100213 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000214 ERR.info(m())
215 ERR.debug(m())
216
217 INF.debug(m())
218
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000219 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000220 ('ERR', 'CRITICAL', '1'),
221 ('ERR', 'ERROR', '2'),
222 ('INF', 'CRITICAL', '3'),
223 ('INF', 'ERROR', '4'),
224 ('INF', 'WARNING', '5'),
225 ('INF', 'INFO', '6'),
226 ('DEB', 'CRITICAL', '7'),
227 ('DEB', 'ERROR', '8'),
228 ('DEB', 'WARNING', '9'),
229 ('DEB', 'INFO', '10'),
230 ('DEB', 'DEBUG', '11'),
231 ])
232
233 def test_nested_explicit(self):
234 # Logging levels in a nested namespace, all explicitly set.
235 m = self.next_message
236
237 INF = logging.getLogger("INF")
238 INF.setLevel(logging.INFO)
239 INF_ERR = logging.getLogger("INF.ERR")
240 INF_ERR.setLevel(logging.ERROR)
241
242 # These should log.
243 INF_ERR.log(logging.CRITICAL, m())
244 INF_ERR.error(m())
245
246 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100247 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000248 INF_ERR.info(m())
249 INF_ERR.debug(m())
250
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000251 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000252 ('INF.ERR', 'CRITICAL', '1'),
253 ('INF.ERR', 'ERROR', '2'),
254 ])
255
256 def test_nested_inherited(self):
257 #Logging levels in a nested namespace, inherited from parent loggers.
258 m = self.next_message
259
260 INF = logging.getLogger("INF")
261 INF.setLevel(logging.INFO)
262 INF_ERR = logging.getLogger("INF.ERR")
263 INF_ERR.setLevel(logging.ERROR)
264 INF_UNDEF = logging.getLogger("INF.UNDEF")
265 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
266 UNDEF = logging.getLogger("UNDEF")
267
268 # These should log.
269 INF_UNDEF.log(logging.CRITICAL, m())
270 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100271 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000272 INF_UNDEF.info(m())
273 INF_ERR_UNDEF.log(logging.CRITICAL, m())
274 INF_ERR_UNDEF.error(m())
275
276 # These should not log.
277 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100278 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000279 INF_ERR_UNDEF.info(m())
280 INF_ERR_UNDEF.debug(m())
281
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000282 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000283 ('INF.UNDEF', 'CRITICAL', '1'),
284 ('INF.UNDEF', 'ERROR', '2'),
285 ('INF.UNDEF', 'WARNING', '3'),
286 ('INF.UNDEF', 'INFO', '4'),
287 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
288 ('INF.ERR.UNDEF', 'ERROR', '6'),
289 ])
290
291 def test_nested_with_virtual_parent(self):
292 # Logging levels when some parent does not exist yet.
293 m = self.next_message
294
295 INF = logging.getLogger("INF")
296 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
297 CHILD = logging.getLogger("INF.BADPARENT")
298 INF.setLevel(logging.INFO)
299
300 # These should log.
301 GRANDCHILD.log(logging.FATAL, m())
302 GRANDCHILD.info(m())
303 CHILD.log(logging.FATAL, m())
304 CHILD.info(m())
305
306 # These should not log.
307 GRANDCHILD.debug(m())
308 CHILD.debug(m())
309
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000310 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000311 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
312 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
313 ('INF.BADPARENT', 'CRITICAL', '3'),
314 ('INF.BADPARENT', 'INFO', '4'),
315 ])
316
317
318class BasicFilterTest(BaseTest):
319
320 """Test the bundled Filter class."""
321
322 def test_filter(self):
323 # Only messages satisfying the specified criteria pass through the
324 # filter.
325 filter_ = logging.Filter("spam.eggs")
326 handler = self.root_logger.handlers[0]
327 try:
328 handler.addFilter(filter_)
329 spam = logging.getLogger("spam")
330 spam_eggs = logging.getLogger("spam.eggs")
331 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
332 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
333
334 spam.info(self.next_message())
335 spam_eggs.info(self.next_message()) # Good.
336 spam_eggs_fish.info(self.next_message()) # Good.
337 spam_bakedbeans.info(self.next_message())
338
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000339 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000340 ('spam.eggs', 'INFO', '2'),
341 ('spam.eggs.fish', 'INFO', '3'),
342 ])
343 finally:
344 handler.removeFilter(filter_)
345
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000346 def test_callable_filter(self):
347 # Only messages satisfying the specified criteria pass through the
348 # filter.
349
350 def filterfunc(record):
351 parts = record.name.split('.')
352 prefix = '.'.join(parts[:2])
353 return prefix == 'spam.eggs'
354
355 handler = self.root_logger.handlers[0]
356 try:
357 handler.addFilter(filterfunc)
358 spam = logging.getLogger("spam")
359 spam_eggs = logging.getLogger("spam.eggs")
360 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
361 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
362
363 spam.info(self.next_message())
364 spam_eggs.info(self.next_message()) # Good.
365 spam_eggs_fish.info(self.next_message()) # Good.
366 spam_bakedbeans.info(self.next_message())
367
368 self.assert_log_lines([
369 ('spam.eggs', 'INFO', '2'),
370 ('spam.eggs.fish', 'INFO', '3'),
371 ])
372 finally:
373 handler.removeFilter(filterfunc)
374
Vinay Sajip985ef872011-04-26 19:34:04 +0100375 def test_empty_filter(self):
376 f = logging.Filter()
377 r = logging.makeLogRecord({'name': 'spam.eggs'})
378 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000379
380#
381# First, we define our levels. There can be as many as you want - the only
382# limitations are that they should be integers, the lowest should be > 0 and
383# larger values mean less information being logged. If you need specific
384# level values which do not fit into these limitations, you can use a
385# mapping dictionary to convert between your application levels and the
386# logging system.
387#
388SILENT = 120
389TACITURN = 119
390TERSE = 118
391EFFUSIVE = 117
392SOCIABLE = 116
393VERBOSE = 115
394TALKATIVE = 114
395GARRULOUS = 113
396CHATTERBOX = 112
397BORING = 111
398
399LEVEL_RANGE = range(BORING, SILENT + 1)
400
401#
402# Next, we define names for our levels. You don't need to do this - in which
403# case the system will use "Level n" to denote the text for the level.
404#
405my_logging_levels = {
406 SILENT : 'Silent',
407 TACITURN : 'Taciturn',
408 TERSE : 'Terse',
409 EFFUSIVE : 'Effusive',
410 SOCIABLE : 'Sociable',
411 VERBOSE : 'Verbose',
412 TALKATIVE : 'Talkative',
413 GARRULOUS : 'Garrulous',
414 CHATTERBOX : 'Chatterbox',
415 BORING : 'Boring',
416}
417
418class GarrulousFilter(logging.Filter):
419
420 """A filter which blocks garrulous messages."""
421
422 def filter(self, record):
423 return record.levelno != GARRULOUS
424
425class VerySpecificFilter(logging.Filter):
426
427 """A filter which blocks sociable and taciturn messages."""
428
429 def filter(self, record):
430 return record.levelno not in [SOCIABLE, TACITURN]
431
432
433class CustomLevelsAndFiltersTest(BaseTest):
434
435 """Test various filtering possibilities with custom logging levels."""
436
437 # Skip the logger name group.
438 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
439
440 def setUp(self):
441 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000442 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000443 logging.addLevelName(k, v)
444
445 def log_at_all_levels(self, logger):
446 for lvl in LEVEL_RANGE:
447 logger.log(lvl, self.next_message())
448
449 def test_logger_filter(self):
450 # Filter at logger level.
451 self.root_logger.setLevel(VERBOSE)
452 # Levels >= 'Verbose' are good.
453 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000454 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000455 ('Verbose', '5'),
456 ('Sociable', '6'),
457 ('Effusive', '7'),
458 ('Terse', '8'),
459 ('Taciturn', '9'),
460 ('Silent', '10'),
461 ])
462
463 def test_handler_filter(self):
464 # Filter at handler level.
465 self.root_logger.handlers[0].setLevel(SOCIABLE)
466 try:
467 # Levels >= 'Sociable' are good.
468 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000469 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000470 ('Sociable', '6'),
471 ('Effusive', '7'),
472 ('Terse', '8'),
473 ('Taciturn', '9'),
474 ('Silent', '10'),
475 ])
476 finally:
477 self.root_logger.handlers[0].setLevel(logging.NOTSET)
478
479 def test_specific_filters(self):
480 # Set a specific filter object on the handler, and then add another
481 # filter object on the logger itself.
482 handler = self.root_logger.handlers[0]
483 specific_filter = None
484 garr = GarrulousFilter()
485 handler.addFilter(garr)
486 try:
487 self.log_at_all_levels(self.root_logger)
488 first_lines = [
489 # Notice how 'Garrulous' is missing
490 ('Boring', '1'),
491 ('Chatterbox', '2'),
492 ('Talkative', '4'),
493 ('Verbose', '5'),
494 ('Sociable', '6'),
495 ('Effusive', '7'),
496 ('Terse', '8'),
497 ('Taciturn', '9'),
498 ('Silent', '10'),
499 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000500 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000501
502 specific_filter = VerySpecificFilter()
503 self.root_logger.addFilter(specific_filter)
504 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000505 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000506 # Not only 'Garrulous' is still missing, but also 'Sociable'
507 # and 'Taciturn'
508 ('Boring', '11'),
509 ('Chatterbox', '12'),
510 ('Talkative', '14'),
511 ('Verbose', '15'),
512 ('Effusive', '17'),
513 ('Terse', '18'),
514 ('Silent', '20'),
515 ])
516 finally:
517 if specific_filter:
518 self.root_logger.removeFilter(specific_filter)
519 handler.removeFilter(garr)
520
521
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100522class HandlerTest(BaseTest):
523 def test_name(self):
524 h = logging.Handler()
525 h.name = 'generic'
526 self.assertEqual(h.name, 'generic')
527 h.name = 'anothergeneric'
528 self.assertEqual(h.name, 'anothergeneric')
529 self.assertRaises(NotImplementedError, h.emit, None)
530
Vinay Sajip5a35b062011-04-27 11:31:14 +0100531 def test_builtin_handlers(self):
532 # We can't actually *use* too many handlers in the tests,
533 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200534 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100535 for existing in (True, False):
536 fd, fn = tempfile.mkstemp()
537 os.close(fd)
538 if not existing:
539 os.unlink(fn)
540 h = logging.handlers.WatchedFileHandler(fn, delay=True)
541 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100542 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100543 self.assertEqual(dev, -1)
544 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100545 r = logging.makeLogRecord({'msg': 'Test'})
546 h.handle(r)
547 # Now remove the file.
548 os.unlink(fn)
549 self.assertFalse(os.path.exists(fn))
550 # The next call should recreate the file.
551 h.handle(r)
552 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100553 else:
554 self.assertEqual(h.dev, -1)
555 self.assertEqual(h.ino, -1)
556 h.close()
557 if existing:
558 os.unlink(fn)
559 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100560 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100561 else:
562 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100563 try:
564 h = logging.handlers.SysLogHandler(sockname)
565 self.assertEqual(h.facility, h.LOG_USER)
566 self.assertTrue(h.unixsocket)
567 h.close()
568 except socket.error: # syslogd might not be available
569 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100570 for method in ('GET', 'POST', 'PUT'):
571 if method == 'PUT':
572 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
573 'localhost', '/log', method)
574 else:
575 h = logging.handlers.HTTPHandler('localhost', '/log', method)
576 h.close()
577 h = logging.handlers.BufferingHandler(0)
578 r = logging.makeLogRecord({})
579 self.assertTrue(h.shouldFlush(r))
580 h.close()
581 h = logging.handlers.BufferingHandler(1)
582 self.assertFalse(h.shouldFlush(r))
583 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100584
Vinay Sajip5e86eed2012-05-29 22:48:10 +0100585 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100586 @unittest.skipUnless(threading, 'Threading required for this test.')
587 def test_race(self):
588 # Issue #14632 refers.
589 def remove_loop(fname, tries):
590 for _ in range(tries):
591 try:
592 os.unlink(fname)
593 except OSError:
594 pass
595 time.sleep(0.004 * random.randint(0, 4))
596
Vinay Sajipc94871a2012-04-25 10:51:35 +0100597 del_count = 500
598 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100599
Vinay Sajipa5798de2012-04-24 23:33:33 +0100600 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100601 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
602 os.close(fd)
603 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
604 remover.daemon = True
605 remover.start()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100606 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100607 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
608 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100609 try:
610 for _ in range(log_count):
611 time.sleep(0.005)
612 r = logging.makeLogRecord({'msg': 'testing' })
613 h.handle(r)
614 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100615 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100616 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100617 if os.path.exists(fn):
618 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100619
620
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100621class BadStream(object):
622 def write(self, data):
623 raise RuntimeError('deliberate mistake')
624
625class TestStreamHandler(logging.StreamHandler):
626 def handleError(self, record):
627 self.error_record = record
628
629class StreamHandlerTest(BaseTest):
630 def test_error_handling(self):
631 h = TestStreamHandler(BadStream())
632 r = logging.makeLogRecord({})
633 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100634 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100635 try:
636 h.handle(r)
637 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100638 h = logging.StreamHandler(BadStream())
639 sys.stderr = sio = io.StringIO()
640 h.handle(r)
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100641 self.assertIn('\nRuntimeError: deliberate mistake\n',
642 sio.getvalue())
Vinay Sajip985ef872011-04-26 19:34:04 +0100643 logging.raiseExceptions = False
644 sys.stderr = sio = io.StringIO()
645 h.handle(r)
646 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100647 finally:
648 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100649 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100650
Vinay Sajip7367d082011-05-02 13:17:27 +0100651# -- The following section could be moved into a server_helper.py module
652# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100653
Vinay Sajipce7c9782011-05-17 07:15:53 +0100654if threading:
655 class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100656 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100657 This derived class has had to be created because smtpd does not
658 support use of custom channel maps, although they are allowed by
659 asyncore's design. Issue #11959 has been raised to address this,
660 and if resolved satisfactorily, some of this code can be removed.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100661 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100662 def __init__(self, server, conn, addr, sockmap):
663 asynchat.async_chat.__init__(self, conn, sockmap)
664 self.smtp_server = server
665 self.conn = conn
666 self.addr = addr
R David Murrayd1a30c92012-05-26 14:33:59 -0400667 self.data_size_limit = None
Vinay Sajipce7c9782011-05-17 07:15:53 +0100668 self.received_lines = []
669 self.smtp_state = self.COMMAND
670 self.seen_greeting = ''
671 self.mailfrom = None
672 self.rcpttos = []
673 self.received_data = ''
674 self.fqdn = socket.getfqdn()
675 self.num_bytes = 0
676 try:
677 self.peer = conn.getpeername()
678 except socket.error as err:
679 # a race condition may occur if the other end is closing
680 # before we can get the peername
681 self.close()
682 if err.args[0] != errno.ENOTCONN:
683 raise
684 return
685 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
686 self.set_terminator(b'\r\n')
R David Murrayd1a30c92012-05-26 14:33:59 -0400687 self.extended_smtp = False
Vinay Sajipa463d252011-04-30 21:52:48 +0100688
Vinay Sajip314b92b2011-05-02 14:31:16 +0100689
Vinay Sajipce7c9782011-05-17 07:15:53 +0100690 class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100691 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100692 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100693
Vinay Sajipce7c9782011-05-17 07:15:53 +0100694 :param addr: A (host, port) tuple which the server listens on.
695 You can specify a port value of zero: the server's
696 *port* attribute will hold the actual port number
697 used, which can be used in client connections.
698 :param handler: A callable which will be called to process
699 incoming messages. The handler will be passed
700 the client address tuple, who the message is from,
701 a list of recipients and the message data.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100702 :param poll_interval: The interval, in seconds, used in the underlying
703 :func:`select` or :func:`poll` call by
704 :func:`asyncore.loop`.
Vinay Sajipce7c9782011-05-17 07:15:53 +0100705 :param sockmap: A dictionary which will be used to hold
706 :class:`asyncore.dispatcher` instances used by
707 :func:`asyncore.loop`. This avoids changing the
708 :mod:`asyncore` module's global state.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100709 """
Vinay Sajipce7c9782011-05-17 07:15:53 +0100710 channel_class = TestSMTPChannel
711
712 def __init__(self, addr, handler, poll_interval, sockmap):
713 self._localaddr = addr
714 self._remoteaddr = None
R David Murrayd1a30c92012-05-26 14:33:59 -0400715 self.data_size_limit = None
Vinay Sajipce7c9782011-05-17 07:15:53 +0100716 self.sockmap = sockmap
717 asyncore.dispatcher.__init__(self, map=sockmap)
718 try:
719 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
720 sock.setblocking(0)
721 self.set_socket(sock, map=sockmap)
722 # try to re-use a server port if possible
723 self.set_reuse_addr()
724 self.bind(addr)
725 self.port = sock.getsockname()[1]
726 self.listen(5)
727 except:
728 self.close()
Vinay Sajipe73afad2011-05-10 07:48:28 +0100729 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +0100730 self._handler = handler
731 self._thread = None
732 self.poll_interval = poll_interval
Vinay Sajipa463d252011-04-30 21:52:48 +0100733
Vinay Sajipce7c9782011-05-17 07:15:53 +0100734 def handle_accepted(self, conn, addr):
735 """
736 Redefined only because the base class does not pass in a
737 map, forcing use of a global in :mod:`asyncore`.
738 """
739 channel = self.channel_class(self, conn, addr, self.sockmap)
Vinay Sajip314b92b2011-05-02 14:31:16 +0100740
Vinay Sajipce7c9782011-05-17 07:15:53 +0100741 def process_message(self, peer, mailfrom, rcpttos, data):
742 """
743 Delegates to the handler passed in to the server's constructor.
Vinay Sajipa463d252011-04-30 21:52:48 +0100744
Vinay Sajipce7c9782011-05-17 07:15:53 +0100745 Typically, this will be a test case method.
746 :param peer: The client (host, port) tuple.
747 :param mailfrom: The address of the sender.
748 :param rcpttos: The addresses of the recipients.
749 :param data: The message.
750 """
751 self._handler(peer, mailfrom, rcpttos, data)
Vinay Sajip7367d082011-05-02 13:17:27 +0100752
Vinay Sajipce7c9782011-05-17 07:15:53 +0100753 def start(self):
754 """
755 Start the server running on a separate daemon thread.
756 """
757 self._thread = t = threading.Thread(target=self.serve_forever,
758 args=(self.poll_interval,))
759 t.setDaemon(True)
760 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100761
Vinay Sajipce7c9782011-05-17 07:15:53 +0100762 def serve_forever(self, poll_interval):
763 """
764 Run the :mod:`asyncore` loop until normal termination
765 conditions arise.
766 :param poll_interval: The interval, in seconds, used in the underlying
767 :func:`select` or :func:`poll` call by
768 :func:`asyncore.loop`.
769 """
770 try:
771 asyncore.loop(poll_interval, map=self.sockmap)
772 except select.error:
773 # On FreeBSD 8, closing the server repeatably
774 # raises this error. We swallow it if the
775 # server has been closed.
776 if self.connected or self.accepting:
777 raise
Vinay Sajip7367d082011-05-02 13:17:27 +0100778
Vinay Sajipce7c9782011-05-17 07:15:53 +0100779 def stop(self, timeout=None):
780 """
781 Stop the thread by closing the server instance.
782 Wait for the server thread to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100783
Vinay Sajipce7c9782011-05-17 07:15:53 +0100784 :param timeout: How long to wait for the server thread
785 to terminate.
786 """
787 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100788 self._thread.join(timeout)
789 self._thread = None
Vinay Sajip7367d082011-05-02 13:17:27 +0100790
Vinay Sajipce7c9782011-05-17 07:15:53 +0100791 class ControlMixin(object):
792 """
793 This mixin is used to start a server on a separate thread, and
794 shut it down programmatically. Request handling is simplified - instead
795 of needing to derive a suitable RequestHandler subclass, you just
796 provide a callable which will be passed each received request to be
797 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100798
Vinay Sajipce7c9782011-05-17 07:15:53 +0100799 :param handler: A handler callable which will be called with a
800 single parameter - the request - in order to
801 process the request. This handler is called on the
802 server thread, effectively meaning that requests are
803 processed serially. While not quite Web scale ;-),
804 this should be fine for testing applications.
805 :param poll_interval: The polling interval in seconds.
806 """
807 def __init__(self, handler, poll_interval):
808 self._thread = None
809 self.poll_interval = poll_interval
810 self._handler = handler
811 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100812
Vinay Sajipce7c9782011-05-17 07:15:53 +0100813 def start(self):
814 """
815 Create a daemon thread to run the server, and start it.
816 """
817 self._thread = t = threading.Thread(target=self.serve_forever,
818 args=(self.poll_interval,))
819 t.setDaemon(True)
820 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100821
Vinay Sajipce7c9782011-05-17 07:15:53 +0100822 def serve_forever(self, poll_interval):
823 """
824 Run the server. Set the ready flag before entering the
825 service loop.
826 """
827 self.ready.set()
828 super(ControlMixin, self).serve_forever(poll_interval)
Vinay Sajip7367d082011-05-02 13:17:27 +0100829
Vinay Sajipce7c9782011-05-17 07:15:53 +0100830 def stop(self, timeout=None):
831 """
832 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip7367d082011-05-02 13:17:27 +0100833
Vinay Sajipce7c9782011-05-17 07:15:53 +0100834 :param timeout: How long to wait for the server thread
835 to terminate.
836 """
837 self.shutdown()
838 if self._thread is not None:
839 self._thread.join(timeout)
840 self._thread = None
841 self.server_close()
842 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100843
Vinay Sajipce7c9782011-05-17 07:15:53 +0100844 class TestHTTPServer(ControlMixin, HTTPServer):
845 """
846 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100847
Vinay Sajipce7c9782011-05-17 07:15:53 +0100848 :param addr: A tuple with the IP address and port to listen on.
849 :param handler: A handler callable which will be called with a
850 single parameter - the request - in order to
851 process the request.
852 :param poll_interval: The polling interval in seconds.
853 :param log: Pass ``True`` to enable log messages.
854 """
Vinay Sajip32565b62011-05-21 00:34:51 +0100855 def __init__(self, addr, handler, poll_interval=0.5,
856 log=False, sslctx=None):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100857 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
858 def __getattr__(self, name, default=None):
859 if name.startswith('do_'):
860 return self.process_request
861 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100862
Vinay Sajipce7c9782011-05-17 07:15:53 +0100863 def process_request(self):
864 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100865
Vinay Sajipce7c9782011-05-17 07:15:53 +0100866 def log_message(self, format, *args):
867 if log:
868 super(DelegatingHTTPRequestHandler,
869 self).log_message(format, *args)
870 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
871 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100872 self.sslctx = sslctx
873
874 def get_request(self):
875 try:
876 sock, addr = self.socket.accept()
877 if self.sslctx:
878 sock = self.sslctx.wrap_socket(sock, server_side=True)
879 except socket.error as e:
880 # socket errors are silenced by the caller, print them here
881 sys.stderr.write("Got an error:\n%s\n" % e)
882 raise
883 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100884
Vinay Sajipce7c9782011-05-17 07:15:53 +0100885 class TestTCPServer(ControlMixin, ThreadingTCPServer):
886 """
887 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100888
Vinay Sajipce7c9782011-05-17 07:15:53 +0100889 :param addr: A tuple with the IP address and port to listen on.
890 :param handler: A handler callable which will be called with a single
891 parameter - the request - in order to process the request.
892 :param poll_interval: The polling interval in seconds.
893 :bind_and_activate: If True (the default), binds the server and starts it
894 listening. If False, you need to call
895 :meth:`server_bind` and :meth:`server_activate` at
896 some later time before calling :meth:`start`, so that
897 the server will set up the socket and listen on it.
898 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100899
Vinay Sajipce7c9782011-05-17 07:15:53 +0100900 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100901
Vinay Sajipce7c9782011-05-17 07:15:53 +0100902 def __init__(self, addr, handler, poll_interval=0.5,
903 bind_and_activate=True):
904 class DelegatingTCPRequestHandler(StreamRequestHandler):
905
906 def handle(self):
907 self.server._handler(self)
908 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
909 bind_and_activate)
910 ControlMixin.__init__(self, handler, poll_interval)
911
912 def server_bind(self):
913 super(TestTCPServer, self).server_bind()
914 self.port = self.socket.getsockname()[1]
915
916 class TestUDPServer(ControlMixin, ThreadingUDPServer):
917 """
918 A UDP server which is controllable using :class:`ControlMixin`.
919
920 :param addr: A tuple with the IP address and port to listen on.
921 :param handler: A handler callable which will be called with a
922 single parameter - the request - in order to
923 process the request.
924 :param poll_interval: The polling interval for shutdown requests,
925 in seconds.
926 :bind_and_activate: If True (the default), binds the server and
927 starts it listening. If False, you need to
928 call :meth:`server_bind` and
929 :meth:`server_activate` at some later time
930 before calling :meth:`start`, so that the server will
931 set up the socket and listen on it.
932 """
Vinay Sajip3ef12292011-05-23 23:00:42 +0100933 def __init__(self, addr, handler, poll_interval=0.5,
934 bind_and_activate=True):
Vinay Sajipce7c9782011-05-17 07:15:53 +0100935 class DelegatingUDPRequestHandler(DatagramRequestHandler):
936
937 def handle(self):
938 self.server._handler(self)
Vinay Sajipba980db2011-05-23 21:37:54 +0100939
940 def finish(self):
941 data = self.wfile.getvalue()
Vinay Sajip3ef12292011-05-23 23:00:42 +0100942 if data:
943 try:
944 super(DelegatingUDPRequestHandler, self).finish()
945 except socket.error:
946 if not self.server._closed:
947 raise
Vinay Sajipba980db2011-05-23 21:37:54 +0100948
Vinay Sajip3ef12292011-05-23 23:00:42 +0100949 ThreadingUDPServer.__init__(self, addr,
950 DelegatingUDPRequestHandler,
Vinay Sajipce7c9782011-05-17 07:15:53 +0100951 bind_and_activate)
952 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajipba980db2011-05-23 21:37:54 +0100953 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +0100954
955 def server_bind(self):
956 super(TestUDPServer, self).server_bind()
957 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100958
Vinay Sajipba980db2011-05-23 21:37:54 +0100959 def server_close(self):
960 super(TestUDPServer, self).server_close()
961 self._closed = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100962
963# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100964
Vinay Sajipce7c9782011-05-17 07:15:53 +0100965@unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipa463d252011-04-30 21:52:48 +0100966class SMTPHandlerTest(BaseTest):
967 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100968 sockmap = {}
969 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
970 sockmap)
971 server.start()
972 addr = ('localhost', server.port)
Vinay Sajip4c3f4782012-04-22 18:19:50 +0100973 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log', timeout=5.0)
Vinay Sajipa463d252011-04-30 21:52:48 +0100974 self.assertEqual(h.toaddrs, ['you'])
975 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100976 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100977 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100978 h.handle(r)
Vinay Sajip17160fd2012-03-15 12:02:08 +0000979 self.handled.wait(5.0) # 14314: don't wait forever
Vinay Sajipa463d252011-04-30 21:52:48 +0100980 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +0000981 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +0100982 self.assertEqual(len(self.messages), 1)
983 peer, mailfrom, rcpttos, data = self.messages[0]
984 self.assertEqual(mailfrom, 'me')
985 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100986 self.assertIn('\nSubject: Log\n', data)
Vinay Sajipa463d252011-04-30 21:52:48 +0100987 self.assertTrue(data.endswith('\n\nHello'))
988 h.close()
989
990 def process_message(self, *args):
991 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100992 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100993
Christian Heimes180510d2008-03-03 19:15:45 +0000994class MemoryHandlerTest(BaseTest):
995
996 """Tests for the MemoryHandler."""
997
998 # Do not bother with a logger name group.
999 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
1000
1001 def setUp(self):
1002 BaseTest.setUp(self)
1003 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1004 self.root_hdlr)
1005 self.mem_logger = logging.getLogger('mem')
1006 self.mem_logger.propagate = 0
1007 self.mem_logger.addHandler(self.mem_hdlr)
1008
1009 def tearDown(self):
1010 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001011 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001012
1013 def test_flush(self):
1014 # The memory handler flushes to its target handler based on specific
1015 # criteria (message count and message level).
1016 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001017 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001018 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001019 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001020 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001021 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001022 lines = [
1023 ('DEBUG', '1'),
1024 ('INFO', '2'),
1025 ('WARNING', '3'),
1026 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001027 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001028 for n in (4, 14):
1029 for i in range(9):
1030 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001031 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001032 # This will flush because it's the 10th message since the last
1033 # flush.
1034 self.mem_logger.debug(self.next_message())
1035 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001036 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001037
1038 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001039 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001040
1041
1042class ExceptionFormatter(logging.Formatter):
1043 """A special exception formatter."""
1044 def formatException(self, ei):
1045 return "Got a [%s]" % ei[0].__name__
1046
1047
1048class ConfigFileTest(BaseTest):
1049
1050 """Reading logging config from a .ini-style config file."""
1051
1052 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1053
1054 # config0 is a standard configuration.
1055 config0 = """
1056 [loggers]
1057 keys=root
1058
1059 [handlers]
1060 keys=hand1
1061
1062 [formatters]
1063 keys=form1
1064
1065 [logger_root]
1066 level=WARNING
1067 handlers=hand1
1068
1069 [handler_hand1]
1070 class=StreamHandler
1071 level=NOTSET
1072 formatter=form1
1073 args=(sys.stdout,)
1074
1075 [formatter_form1]
1076 format=%(levelname)s ++ %(message)s
1077 datefmt=
1078 """
1079
1080 # config1 adds a little to the standard configuration.
1081 config1 = """
1082 [loggers]
1083 keys=root,parser
1084
1085 [handlers]
1086 keys=hand1
1087
1088 [formatters]
1089 keys=form1
1090
1091 [logger_root]
1092 level=WARNING
1093 handlers=
1094
1095 [logger_parser]
1096 level=DEBUG
1097 handlers=hand1
1098 propagate=1
1099 qualname=compiler.parser
1100
1101 [handler_hand1]
1102 class=StreamHandler
1103 level=NOTSET
1104 formatter=form1
1105 args=(sys.stdout,)
1106
1107 [formatter_form1]
1108 format=%(levelname)s ++ %(message)s
1109 datefmt=
1110 """
1111
Vinay Sajip3f84b072011-03-07 17:49:33 +00001112 # config1a moves the handler to the root.
1113 config1a = """
1114 [loggers]
1115 keys=root,parser
1116
1117 [handlers]
1118 keys=hand1
1119
1120 [formatters]
1121 keys=form1
1122
1123 [logger_root]
1124 level=WARNING
1125 handlers=hand1
1126
1127 [logger_parser]
1128 level=DEBUG
1129 handlers=
1130 propagate=1
1131 qualname=compiler.parser
1132
1133 [handler_hand1]
1134 class=StreamHandler
1135 level=NOTSET
1136 formatter=form1
1137 args=(sys.stdout,)
1138
1139 [formatter_form1]
1140 format=%(levelname)s ++ %(message)s
1141 datefmt=
1142 """
1143
Christian Heimes180510d2008-03-03 19:15:45 +00001144 # config2 has a subtle configuration error that should be reported
1145 config2 = config1.replace("sys.stdout", "sys.stbout")
1146
1147 # config3 has a less subtle configuration error
1148 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1149
1150 # config4 specifies a custom formatter class to be loaded
1151 config4 = """
1152 [loggers]
1153 keys=root
1154
1155 [handlers]
1156 keys=hand1
1157
1158 [formatters]
1159 keys=form1
1160
1161 [logger_root]
1162 level=NOTSET
1163 handlers=hand1
1164
1165 [handler_hand1]
1166 class=StreamHandler
1167 level=NOTSET
1168 formatter=form1
1169 args=(sys.stdout,)
1170
1171 [formatter_form1]
1172 class=""" + __name__ + """.ExceptionFormatter
1173 format=%(levelname)s:%(name)s:%(message)s
1174 datefmt=
1175 """
1176
Georg Brandl3dbca812008-07-23 16:10:53 +00001177 # config5 specifies a custom handler class to be loaded
1178 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1179
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001180 # config6 uses ', ' delimiters in the handlers and formatters sections
1181 config6 = """
1182 [loggers]
1183 keys=root,parser
1184
1185 [handlers]
1186 keys=hand1, hand2
1187
1188 [formatters]
1189 keys=form1, form2
1190
1191 [logger_root]
1192 level=WARNING
1193 handlers=
1194
1195 [logger_parser]
1196 level=DEBUG
1197 handlers=hand1
1198 propagate=1
1199 qualname=compiler.parser
1200
1201 [handler_hand1]
1202 class=StreamHandler
1203 level=NOTSET
1204 formatter=form1
1205 args=(sys.stdout,)
1206
1207 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001208 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001209 level=NOTSET
1210 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001211 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001212
1213 [formatter_form1]
1214 format=%(levelname)s ++ %(message)s
1215 datefmt=
1216
1217 [formatter_form2]
1218 format=%(message)s
1219 datefmt=
1220 """
1221
Vinay Sajip3f84b072011-03-07 17:49:33 +00001222 # config7 adds a compiler logger.
1223 config7 = """
1224 [loggers]
1225 keys=root,parser,compiler
1226
1227 [handlers]
1228 keys=hand1
1229
1230 [formatters]
1231 keys=form1
1232
1233 [logger_root]
1234 level=WARNING
1235 handlers=hand1
1236
1237 [logger_compiler]
1238 level=DEBUG
1239 handlers=
1240 propagate=1
1241 qualname=compiler
1242
1243 [logger_parser]
1244 level=DEBUG
1245 handlers=
1246 propagate=1
1247 qualname=compiler.parser
1248
1249 [handler_hand1]
1250 class=StreamHandler
1251 level=NOTSET
1252 formatter=form1
1253 args=(sys.stdout,)
1254
1255 [formatter_form1]
1256 format=%(levelname)s ++ %(message)s
1257 datefmt=
1258 """
1259
Christian Heimes180510d2008-03-03 19:15:45 +00001260 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001261 file = io.StringIO(textwrap.dedent(conf))
1262 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001263
1264 def test_config0_ok(self):
1265 # A simple config file which overrides the default settings.
1266 with captured_stdout() as output:
1267 self.apply_config(self.config0)
1268 logger = logging.getLogger()
1269 # Won't output anything
1270 logger.info(self.next_message())
1271 # Outputs a message
1272 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001273 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001274 ('ERROR', '2'),
1275 ], stream=output)
1276 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001277 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001278
Georg Brandl3dbca812008-07-23 16:10:53 +00001279 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001280 # A config file defining a sub-parser as well.
1281 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001282 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001283 logger = logging.getLogger("compiler.parser")
1284 # Both will output a message
1285 logger.info(self.next_message())
1286 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001287 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001288 ('INFO', '1'),
1289 ('ERROR', '2'),
1290 ], stream=output)
1291 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001292 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001293
1294 def test_config2_failure(self):
1295 # A simple config file which overrides the default settings.
1296 self.assertRaises(Exception, self.apply_config, self.config2)
1297
1298 def test_config3_failure(self):
1299 # A simple config file which overrides the default settings.
1300 self.assertRaises(Exception, self.apply_config, self.config3)
1301
1302 def test_config4_ok(self):
1303 # A config file specifying a custom formatter class.
1304 with captured_stdout() as output:
1305 self.apply_config(self.config4)
1306 logger = logging.getLogger()
1307 try:
1308 raise RuntimeError()
1309 except RuntimeError:
1310 logging.exception("just testing")
1311 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001312 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001313 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1314 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001315 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001316
Georg Brandl3dbca812008-07-23 16:10:53 +00001317 def test_config5_ok(self):
1318 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001319
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001320 def test_config6_ok(self):
1321 self.test_config1_ok(config=self.config6)
1322
Vinay Sajip3f84b072011-03-07 17:49:33 +00001323 def test_config7_ok(self):
1324 with captured_stdout() as output:
1325 self.apply_config(self.config1a)
1326 logger = logging.getLogger("compiler.parser")
1327 # See issue #11424. compiler-hyphenated sorts
1328 # between compiler and compiler.xyz and this
1329 # was preventing compiler.xyz from being included
1330 # in the child loggers of compiler because of an
1331 # overzealous loop termination condition.
1332 hyphenated = logging.getLogger('compiler-hyphenated')
1333 # All will output a message
1334 logger.info(self.next_message())
1335 logger.error(self.next_message())
1336 hyphenated.critical(self.next_message())
1337 self.assert_log_lines([
1338 ('INFO', '1'),
1339 ('ERROR', '2'),
1340 ('CRITICAL', '3'),
1341 ], stream=output)
1342 # Original logger output is empty.
1343 self.assert_log_lines([])
1344 with captured_stdout() as output:
1345 self.apply_config(self.config7)
1346 logger = logging.getLogger("compiler.parser")
1347 self.assertFalse(logger.disabled)
1348 # Both will output a message
1349 logger.info(self.next_message())
1350 logger.error(self.next_message())
1351 logger = logging.getLogger("compiler.lexer")
1352 # Both will output a message
1353 logger.info(self.next_message())
1354 logger.error(self.next_message())
1355 # Will not appear
1356 hyphenated.critical(self.next_message())
1357 self.assert_log_lines([
1358 ('INFO', '4'),
1359 ('ERROR', '5'),
1360 ('INFO', '6'),
1361 ('ERROR', '7'),
1362 ], stream=output)
1363 # Original logger output is empty.
1364 self.assert_log_lines([])
1365
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001366
Victor Stinner45df8202010-04-28 22:31:17 +00001367@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001368class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001369
Christian Heimes180510d2008-03-03 19:15:45 +00001370 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001371
Christian Heimes180510d2008-03-03 19:15:45 +00001372 def setUp(self):
1373 """Set up a TCP server to receive log messages, and a SocketHandler
1374 pointing to that server's address and port."""
1375 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001376 addr = ('localhost', 0)
1377 self.server = server = TestTCPServer(addr, self.handle_socket,
1378 0.01)
1379 server.start()
1380 server.ready.wait()
1381 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1382 server.port)
1383 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001384 self.root_logger.removeHandler(self.root_logger.handlers[0])
1385 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001386 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001387
Christian Heimes180510d2008-03-03 19:15:45 +00001388 def tearDown(self):
1389 """Shutdown the TCP server."""
1390 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001391 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001392 self.root_logger.removeHandler(self.sock_hdlr)
1393 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001394 finally:
1395 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001396
Vinay Sajip7367d082011-05-02 13:17:27 +01001397 def handle_socket(self, request):
1398 conn = request.connection
1399 while True:
1400 chunk = conn.recv(4)
1401 if len(chunk) < 4:
1402 break
1403 slen = struct.unpack(">L", chunk)[0]
1404 chunk = conn.recv(slen)
1405 while len(chunk) < slen:
1406 chunk = chunk + conn.recv(slen - len(chunk))
1407 obj = pickle.loads(chunk)
1408 record = logging.makeLogRecord(obj)
1409 self.log_output += record.msg + '\n'
1410 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001411
Christian Heimes180510d2008-03-03 19:15:45 +00001412 def test_output(self):
1413 # The log message sent to the SocketHandler is properly received.
1414 logger = logging.getLogger("tcp")
1415 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001416 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001417 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001418 self.handled.acquire()
1419 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001420
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001421 def test_noserver(self):
1422 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001423 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001424 #The logging call should try to connect, which should fail
1425 try:
1426 raise RuntimeError('Deliberate mistake')
1427 except RuntimeError:
1428 self.root_logger.exception('Never sent')
1429 self.root_logger.error('Never sent, either')
1430 now = time.time()
1431 self.assertTrue(self.sock_hdlr.retryTime > now)
1432 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1433 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001434
Vinay Sajip7367d082011-05-02 13:17:27 +01001435
1436@unittest.skipUnless(threading, 'Threading required for this test.')
1437class DatagramHandlerTest(BaseTest):
1438
1439 """Test for DatagramHandler."""
1440
1441 def setUp(self):
1442 """Set up a UDP server to receive log messages, and a DatagramHandler
1443 pointing to that server's address and port."""
1444 BaseTest.setUp(self)
1445 addr = ('localhost', 0)
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001446 self.server = server = TestUDPServer(addr, self.handle_datagram, 0.01)
Vinay Sajip7367d082011-05-02 13:17:27 +01001447 server.start()
1448 server.ready.wait()
1449 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1450 server.port)
1451 self.log_output = ''
1452 self.root_logger.removeHandler(self.root_logger.handlers[0])
1453 self.root_logger.addHandler(self.sock_hdlr)
1454 self.handled = threading.Event()
1455
1456 def tearDown(self):
1457 """Shutdown the UDP server."""
1458 try:
1459 self.server.stop(2.0)
1460 self.root_logger.removeHandler(self.sock_hdlr)
1461 self.sock_hdlr.close()
1462 finally:
1463 BaseTest.tearDown(self)
1464
1465 def handle_datagram(self, request):
1466 slen = struct.pack('>L', 0) # length of prefix
1467 packet = request.packet[len(slen):]
1468 obj = pickle.loads(packet)
1469 record = logging.makeLogRecord(obj)
1470 self.log_output += record.msg + '\n'
1471 self.handled.set()
1472
1473 def test_output(self):
1474 # The log message sent to the DatagramHandler is properly received.
1475 logger = logging.getLogger("udp")
1476 logger.error("spam")
1477 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001478 self.handled.clear()
1479 logger.error("eggs")
1480 self.handled.wait()
1481 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001482
1483
1484@unittest.skipUnless(threading, 'Threading required for this test.')
1485class SysLogHandlerTest(BaseTest):
1486
1487 """Test for SysLogHandler using UDP."""
1488
1489 def setUp(self):
1490 """Set up a UDP server to receive log messages, and a SysLogHandler
1491 pointing to that server's address and port."""
1492 BaseTest.setUp(self)
1493 addr = ('localhost', 0)
1494 self.server = server = TestUDPServer(addr, self.handle_datagram,
1495 0.01)
1496 server.start()
1497 server.ready.wait()
1498 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1499 server.port))
1500 self.log_output = ''
1501 self.root_logger.removeHandler(self.root_logger.handlers[0])
1502 self.root_logger.addHandler(self.sl_hdlr)
1503 self.handled = threading.Event()
1504
1505 def tearDown(self):
1506 """Shutdown the UDP server."""
1507 try:
1508 self.server.stop(2.0)
1509 self.root_logger.removeHandler(self.sl_hdlr)
1510 self.sl_hdlr.close()
1511 finally:
1512 BaseTest.tearDown(self)
1513
1514 def handle_datagram(self, request):
1515 self.log_output = request.packet
1516 self.handled.set()
1517
1518 def test_output(self):
1519 # The log message sent to the SysLogHandler is properly received.
1520 logger = logging.getLogger("slh")
1521 logger.error("sp\xe4m")
1522 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001523 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001524 self.handled.clear()
1525 self.sl_hdlr.append_nul = False
1526 logger.error("sp\xe4m")
1527 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001528 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001529 self.handled.clear()
1530 self.sl_hdlr.ident = "h\xe4m-"
1531 logger.error("sp\xe4m")
1532 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001533 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001534
1535
1536@unittest.skipUnless(threading, 'Threading required for this test.')
1537class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001538 """Test for HTTPHandler."""
1539
Vinay Sajip9ba87612011-05-21 11:32:15 +01001540 PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
1541MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
1542zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
1543aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
1544AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
15450tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
15465LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
1547piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
15482z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
1549i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
1550PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
1551E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
1552rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
1553/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
1554-----END RSA PRIVATE KEY-----
1555-----BEGIN CERTIFICATE-----
1556MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
1557BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
1558EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
1559xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
1560l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
1561vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
1562BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
1563S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
1564BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
15654f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
15661c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
1567IRbTpvlHWPjsSvHz0ZOH
1568-----END CERTIFICATE-----"""
1569
Vinay Sajip7367d082011-05-02 13:17:27 +01001570 def setUp(self):
1571 """Set up an HTTP server to receive log messages, and a HTTPHandler
1572 pointing to that server's address and port."""
1573 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001574 self.handled = threading.Event()
1575
Vinay Sajip7367d082011-05-02 13:17:27 +01001576 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001577 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001578 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001579 if self.command == 'POST':
1580 try:
1581 rlen = int(request.headers['Content-Length'])
1582 self.post_data = request.rfile.read(rlen)
1583 except:
1584 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001585 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001586 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001587 self.handled.set()
1588
1589 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001590 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001591 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001592 root_logger = self.root_logger
1593 root_logger.removeHandler(self.root_logger.handlers[0])
1594 for secure in (False, True):
1595 addr = ('localhost', 0)
1596 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001597 try:
1598 import ssl
1599 fd, fn = tempfile.mkstemp()
1600 os.close(fd)
1601 with open(fn, 'w') as f:
1602 f.write(self.PEMFILE)
1603 sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1604 sslctx.load_cert_chain(fn)
1605 os.unlink(fn)
1606 except ImportError:
1607 sslctx = None
Vinay Sajip0372e102011-05-05 12:59:14 +01001608 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001609 sslctx = None
1610 self.server = server = TestHTTPServer(addr, self.handle_request,
1611 0.01, sslctx=sslctx)
1612 server.start()
1613 server.ready.wait()
1614 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001615 secure_client = secure and sslctx
1616 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
1617 secure=secure_client)
Vinay Sajip9ba87612011-05-21 11:32:15 +01001618 self.log_data = None
1619 root_logger.addHandler(self.h_hdlr)
1620
1621 for method in ('GET', 'POST'):
1622 self.h_hdlr.method = method
1623 self.handled.clear()
1624 msg = "sp\xe4m"
1625 logger.error(msg)
1626 self.handled.wait()
1627 self.assertEqual(self.log_data.path, '/frob')
1628 self.assertEqual(self.command, method)
1629 if method == 'GET':
1630 d = parse_qs(self.log_data.query)
1631 else:
1632 d = parse_qs(self.post_data.decode('utf-8'))
1633 self.assertEqual(d['name'], ['http'])
1634 self.assertEqual(d['funcName'], ['test_output'])
1635 self.assertEqual(d['msg'], [msg])
1636
1637 self.server.stop(2.0)
1638 self.root_logger.removeHandler(self.h_hdlr)
1639 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001640
Christian Heimes180510d2008-03-03 19:15:45 +00001641class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001642
Christian Heimes180510d2008-03-03 19:15:45 +00001643 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001644
Christian Heimes180510d2008-03-03 19:15:45 +00001645 def setUp(self):
1646 """Create a dict to remember potentially destroyed objects."""
1647 BaseTest.setUp(self)
1648 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001649
Christian Heimes180510d2008-03-03 19:15:45 +00001650 def _watch_for_survival(self, *args):
1651 """Watch the given objects for survival, by creating weakrefs to
1652 them."""
1653 for obj in args:
1654 key = id(obj), repr(obj)
1655 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001656
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001657 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001658 """Assert that all objects watched for survival have survived."""
1659 # Trigger cycle breaking.
1660 gc.collect()
1661 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001662 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001663 if ref() is None:
1664 dead.append(repr_)
1665 if dead:
1666 self.fail("%d objects should have survived "
1667 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001668
Christian Heimes180510d2008-03-03 19:15:45 +00001669 def test_persistent_loggers(self):
1670 # Logger objects are persistent and retain their configuration, even
1671 # if visible references are destroyed.
1672 self.root_logger.setLevel(logging.INFO)
1673 foo = logging.getLogger("foo")
1674 self._watch_for_survival(foo)
1675 foo.setLevel(logging.DEBUG)
1676 self.root_logger.debug(self.next_message())
1677 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001678 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001679 ('foo', 'DEBUG', '2'),
1680 ])
1681 del foo
1682 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001683 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001684 # foo has retained its settings.
1685 bar = logging.getLogger("foo")
1686 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001687 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001688 ('foo', 'DEBUG', '2'),
1689 ('foo', 'DEBUG', '3'),
1690 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001691
Benjamin Petersonf91df042009-02-13 02:50:59 +00001692
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001693class EncodingTest(BaseTest):
1694 def test_encoding_plain_file(self):
1695 # In Python 2.x, a plain file object is treated as having no encoding.
1696 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001697 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1698 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001699 # the non-ascii data we write to the log.
1700 data = "foo\x80"
1701 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001702 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001703 log.addHandler(handler)
1704 try:
1705 # write non-ascii data to the log.
1706 log.warning(data)
1707 finally:
1708 log.removeHandler(handler)
1709 handler.close()
1710 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001711 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001712 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001713 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001714 finally:
1715 f.close()
1716 finally:
1717 if os.path.isfile(fn):
1718 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001719
Benjamin Petersonf91df042009-02-13 02:50:59 +00001720 def test_encoding_cyrillic_unicode(self):
1721 log = logging.getLogger("test")
1722 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1723 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1724 #Ensure it's written in a Cyrillic encoding
1725 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001726 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001727 stream = io.BytesIO()
1728 writer = writer_class(stream, 'strict')
1729 handler = logging.StreamHandler(writer)
1730 log.addHandler(handler)
1731 try:
1732 log.warning(message)
1733 finally:
1734 log.removeHandler(handler)
1735 handler.close()
1736 # check we wrote exactly those bytes, ignoring trailing \n etc
1737 s = stream.getvalue()
1738 #Compare against what the data should be when encoded in CP-1251
1739 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1740
1741
Georg Brandlf9734072008-12-07 15:30:06 +00001742class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001743
Georg Brandlf9734072008-12-07 15:30:06 +00001744 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001745 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001746 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001747 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001748 warnings.filterwarnings("always", category=UserWarning)
1749 stream = io.StringIO()
1750 h = logging.StreamHandler(stream)
1751 logger = logging.getLogger("py.warnings")
1752 logger.addHandler(h)
1753 warnings.warn("I'm warning you...")
1754 logger.removeHandler(h)
1755 s = stream.getvalue()
1756 h.close()
1757 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001758
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001759 #See if an explicit file uses the original implementation
1760 a_file = io.StringIO()
1761 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1762 a_file, "Dummy line")
1763 s = a_file.getvalue()
1764 a_file.close()
1765 self.assertEqual(s,
1766 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1767
1768 def test_warnings_no_handlers(self):
1769 with warnings.catch_warnings():
1770 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001771 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001772
1773 # confirm our assumption: no loggers are set
1774 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001775 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001776
1777 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001778 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001779 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001780
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001781
1782def formatFunc(format, datefmt=None):
1783 return logging.Formatter(format, datefmt)
1784
1785def handlerFunc():
1786 return logging.StreamHandler()
1787
1788class CustomHandler(logging.StreamHandler):
1789 pass
1790
1791class ConfigDictTest(BaseTest):
1792
1793 """Reading logging config from a dictionary."""
1794
1795 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1796
1797 # config0 is a standard configuration.
1798 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001799 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001800 'formatters': {
1801 'form1' : {
1802 'format' : '%(levelname)s ++ %(message)s',
1803 },
1804 },
1805 'handlers' : {
1806 'hand1' : {
1807 'class' : 'logging.StreamHandler',
1808 'formatter' : 'form1',
1809 'level' : 'NOTSET',
1810 'stream' : 'ext://sys.stdout',
1811 },
1812 },
1813 'root' : {
1814 'level' : 'WARNING',
1815 'handlers' : ['hand1'],
1816 },
1817 }
1818
1819 # config1 adds a little to the standard configuration.
1820 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001821 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001822 'formatters': {
1823 'form1' : {
1824 'format' : '%(levelname)s ++ %(message)s',
1825 },
1826 },
1827 'handlers' : {
1828 'hand1' : {
1829 'class' : 'logging.StreamHandler',
1830 'formatter' : 'form1',
1831 'level' : 'NOTSET',
1832 'stream' : 'ext://sys.stdout',
1833 },
1834 },
1835 'loggers' : {
1836 'compiler.parser' : {
1837 'level' : 'DEBUG',
1838 'handlers' : ['hand1'],
1839 },
1840 },
1841 'root' : {
1842 'level' : 'WARNING',
1843 },
1844 }
1845
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001846 # config1a moves the handler to the root. Used with config8a
1847 config1a = {
1848 'version': 1,
1849 '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 },
1866 },
1867 'root' : {
1868 'level' : 'WARNING',
1869 'handlers' : ['hand1'],
1870 },
1871 }
1872
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001873 # config2 has a subtle configuration error that should be reported
1874 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001875 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001876 '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.stdbout',
1887 },
1888 },
1889 'loggers' : {
1890 'compiler.parser' : {
1891 'level' : 'DEBUG',
1892 'handlers' : ['hand1'],
1893 },
1894 },
1895 'root' : {
1896 'level' : 'WARNING',
1897 },
1898 }
1899
1900 #As config1 but with a misspelt level on a handler
1901 config2a = {
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' : 'NTOSET',
1913 'stream' : 'ext://sys.stdout',
1914 },
1915 },
1916 'loggers' : {
1917 'compiler.parser' : {
1918 'level' : 'DEBUG',
1919 'handlers' : ['hand1'],
1920 },
1921 },
1922 'root' : {
1923 'level' : 'WARNING',
1924 },
1925 }
1926
1927
1928 #As config1 but with a misspelt level on a logger
1929 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001930 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001931 'formatters': {
1932 'form1' : {
1933 'format' : '%(levelname)s ++ %(message)s',
1934 },
1935 },
1936 'handlers' : {
1937 'hand1' : {
1938 'class' : 'logging.StreamHandler',
1939 'formatter' : 'form1',
1940 'level' : 'NOTSET',
1941 'stream' : 'ext://sys.stdout',
1942 },
1943 },
1944 'loggers' : {
1945 'compiler.parser' : {
1946 'level' : 'DEBUG',
1947 'handlers' : ['hand1'],
1948 },
1949 },
1950 'root' : {
1951 'level' : 'WRANING',
1952 },
1953 }
1954
1955 # config3 has a less subtle configuration error
1956 config3 = {
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' : 'misspelled_name',
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' : 'WARNING',
1979 },
1980 }
1981
1982 # config4 specifies a custom formatter class to be loaded
1983 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001984 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001985 'formatters': {
1986 'form1' : {
1987 '()' : __name__ + '.ExceptionFormatter',
1988 'format' : '%(levelname)s:%(name)s:%(message)s',
1989 },
1990 },
1991 'handlers' : {
1992 'hand1' : {
1993 'class' : 'logging.StreamHandler',
1994 'formatter' : 'form1',
1995 'level' : 'NOTSET',
1996 'stream' : 'ext://sys.stdout',
1997 },
1998 },
1999 'root' : {
2000 'level' : 'NOTSET',
2001 'handlers' : ['hand1'],
2002 },
2003 }
2004
2005 # As config4 but using an actual callable rather than a string
2006 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002007 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002008 'formatters': {
2009 'form1' : {
2010 '()' : ExceptionFormatter,
2011 'format' : '%(levelname)s:%(name)s:%(message)s',
2012 },
2013 'form2' : {
2014 '()' : __name__ + '.formatFunc',
2015 'format' : '%(levelname)s:%(name)s:%(message)s',
2016 },
2017 'form3' : {
2018 '()' : formatFunc,
2019 'format' : '%(levelname)s:%(name)s:%(message)s',
2020 },
2021 },
2022 'handlers' : {
2023 'hand1' : {
2024 'class' : 'logging.StreamHandler',
2025 'formatter' : 'form1',
2026 'level' : 'NOTSET',
2027 'stream' : 'ext://sys.stdout',
2028 },
2029 'hand2' : {
2030 '()' : handlerFunc,
2031 },
2032 },
2033 'root' : {
2034 'level' : 'NOTSET',
2035 'handlers' : ['hand1'],
2036 },
2037 }
2038
2039 # config5 specifies a custom handler class to be loaded
2040 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002041 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002042 'formatters': {
2043 'form1' : {
2044 'format' : '%(levelname)s ++ %(message)s',
2045 },
2046 },
2047 'handlers' : {
2048 'hand1' : {
2049 'class' : __name__ + '.CustomHandler',
2050 'formatter' : 'form1',
2051 'level' : 'NOTSET',
2052 'stream' : 'ext://sys.stdout',
2053 },
2054 },
2055 'loggers' : {
2056 'compiler.parser' : {
2057 'level' : 'DEBUG',
2058 'handlers' : ['hand1'],
2059 },
2060 },
2061 'root' : {
2062 'level' : 'WARNING',
2063 },
2064 }
2065
2066 # config6 specifies a custom handler class to be loaded
2067 # but has bad arguments
2068 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002069 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002070 'formatters': {
2071 'form1' : {
2072 'format' : '%(levelname)s ++ %(message)s',
2073 },
2074 },
2075 'handlers' : {
2076 'hand1' : {
2077 'class' : __name__ + '.CustomHandler',
2078 'formatter' : 'form1',
2079 'level' : 'NOTSET',
2080 'stream' : 'ext://sys.stdout',
2081 '9' : 'invalid parameter name',
2082 },
2083 },
2084 'loggers' : {
2085 'compiler.parser' : {
2086 'level' : 'DEBUG',
2087 'handlers' : ['hand1'],
2088 },
2089 },
2090 'root' : {
2091 'level' : 'WARNING',
2092 },
2093 }
2094
2095 #config 7 does not define compiler.parser but defines compiler.lexer
2096 #so compiler.parser should be disabled after applying it
2097 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002098 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002099 'formatters': {
2100 'form1' : {
2101 'format' : '%(levelname)s ++ %(message)s',
2102 },
2103 },
2104 'handlers' : {
2105 'hand1' : {
2106 'class' : 'logging.StreamHandler',
2107 'formatter' : 'form1',
2108 'level' : 'NOTSET',
2109 'stream' : 'ext://sys.stdout',
2110 },
2111 },
2112 'loggers' : {
2113 'compiler.lexer' : {
2114 'level' : 'DEBUG',
2115 'handlers' : ['hand1'],
2116 },
2117 },
2118 'root' : {
2119 'level' : 'WARNING',
2120 },
2121 }
2122
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002123 # config8 defines both compiler and compiler.lexer
2124 # so compiler.parser should not be disabled (since
2125 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002126 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002127 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002128 'disable_existing_loggers' : False,
2129 'formatters': {
2130 'form1' : {
2131 'format' : '%(levelname)s ++ %(message)s',
2132 },
2133 },
2134 'handlers' : {
2135 'hand1' : {
2136 'class' : 'logging.StreamHandler',
2137 'formatter' : 'form1',
2138 'level' : 'NOTSET',
2139 'stream' : 'ext://sys.stdout',
2140 },
2141 },
2142 'loggers' : {
2143 'compiler' : {
2144 'level' : 'DEBUG',
2145 'handlers' : ['hand1'],
2146 },
2147 'compiler.lexer' : {
2148 },
2149 },
2150 'root' : {
2151 'level' : 'WARNING',
2152 },
2153 }
2154
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002155 # config8a disables existing loggers
2156 config8a = {
2157 'version': 1,
2158 'disable_existing_loggers' : True,
2159 'formatters': {
2160 'form1' : {
2161 'format' : '%(levelname)s ++ %(message)s',
2162 },
2163 },
2164 'handlers' : {
2165 'hand1' : {
2166 'class' : 'logging.StreamHandler',
2167 'formatter' : 'form1',
2168 'level' : 'NOTSET',
2169 'stream' : 'ext://sys.stdout',
2170 },
2171 },
2172 'loggers' : {
2173 'compiler' : {
2174 'level' : 'DEBUG',
2175 'handlers' : ['hand1'],
2176 },
2177 'compiler.lexer' : {
2178 },
2179 },
2180 'root' : {
2181 'level' : 'WARNING',
2182 },
2183 }
2184
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002185 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002186 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002187 'formatters': {
2188 'form1' : {
2189 'format' : '%(levelname)s ++ %(message)s',
2190 },
2191 },
2192 'handlers' : {
2193 'hand1' : {
2194 'class' : 'logging.StreamHandler',
2195 'formatter' : 'form1',
2196 'level' : 'WARNING',
2197 'stream' : 'ext://sys.stdout',
2198 },
2199 },
2200 'loggers' : {
2201 'compiler.parser' : {
2202 'level' : 'WARNING',
2203 'handlers' : ['hand1'],
2204 },
2205 },
2206 'root' : {
2207 'level' : 'NOTSET',
2208 },
2209 }
2210
2211 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002212 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002213 'incremental' : True,
2214 'handlers' : {
2215 'hand1' : {
2216 'level' : 'WARNING',
2217 },
2218 },
2219 'loggers' : {
2220 'compiler.parser' : {
2221 'level' : 'INFO',
2222 },
2223 },
2224 }
2225
2226 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002227 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002228 'incremental' : True,
2229 'handlers' : {
2230 'hand1' : {
2231 'level' : 'INFO',
2232 },
2233 },
2234 'loggers' : {
2235 'compiler.parser' : {
2236 'level' : 'INFO',
2237 },
2238 },
2239 }
2240
2241 #As config1 but with a filter added
2242 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002243 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002244 'formatters': {
2245 'form1' : {
2246 'format' : '%(levelname)s ++ %(message)s',
2247 },
2248 },
2249 'filters' : {
2250 'filt1' : {
2251 'name' : 'compiler.parser',
2252 },
2253 },
2254 'handlers' : {
2255 'hand1' : {
2256 'class' : 'logging.StreamHandler',
2257 'formatter' : 'form1',
2258 'level' : 'NOTSET',
2259 'stream' : 'ext://sys.stdout',
2260 'filters' : ['filt1'],
2261 },
2262 },
2263 'loggers' : {
2264 'compiler.parser' : {
2265 'level' : 'DEBUG',
2266 'filters' : ['filt1'],
2267 },
2268 },
2269 'root' : {
2270 'level' : 'WARNING',
2271 'handlers' : ['hand1'],
2272 },
2273 }
2274
2275 #As config1 but using cfg:// references
2276 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002277 'version': 1,
2278 'true_formatters': {
2279 'form1' : {
2280 'format' : '%(levelname)s ++ %(message)s',
2281 },
2282 },
2283 'handler_configs': {
2284 'hand1' : {
2285 'class' : 'logging.StreamHandler',
2286 'formatter' : 'form1',
2287 'level' : 'NOTSET',
2288 'stream' : 'ext://sys.stdout',
2289 },
2290 },
2291 'formatters' : 'cfg://true_formatters',
2292 'handlers' : {
2293 'hand1' : 'cfg://handler_configs[hand1]',
2294 },
2295 'loggers' : {
2296 'compiler.parser' : {
2297 'level' : 'DEBUG',
2298 'handlers' : ['hand1'],
2299 },
2300 },
2301 'root' : {
2302 'level' : 'WARNING',
2303 },
2304 }
2305
2306 #As config11 but missing the version key
2307 config12 = {
2308 'true_formatters': {
2309 'form1' : {
2310 'format' : '%(levelname)s ++ %(message)s',
2311 },
2312 },
2313 'handler_configs': {
2314 'hand1' : {
2315 'class' : 'logging.StreamHandler',
2316 'formatter' : 'form1',
2317 'level' : 'NOTSET',
2318 'stream' : 'ext://sys.stdout',
2319 },
2320 },
2321 'formatters' : 'cfg://true_formatters',
2322 'handlers' : {
2323 'hand1' : 'cfg://handler_configs[hand1]',
2324 },
2325 'loggers' : {
2326 'compiler.parser' : {
2327 'level' : 'DEBUG',
2328 'handlers' : ['hand1'],
2329 },
2330 },
2331 'root' : {
2332 'level' : 'WARNING',
2333 },
2334 }
2335
2336 #As config11 but using an unsupported version
2337 config13 = {
2338 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002339 'true_formatters': {
2340 'form1' : {
2341 'format' : '%(levelname)s ++ %(message)s',
2342 },
2343 },
2344 'handler_configs': {
2345 'hand1' : {
2346 'class' : 'logging.StreamHandler',
2347 'formatter' : 'form1',
2348 'level' : 'NOTSET',
2349 'stream' : 'ext://sys.stdout',
2350 },
2351 },
2352 'formatters' : 'cfg://true_formatters',
2353 'handlers' : {
2354 'hand1' : 'cfg://handler_configs[hand1]',
2355 },
2356 'loggers' : {
2357 'compiler.parser' : {
2358 'level' : 'DEBUG',
2359 'handlers' : ['hand1'],
2360 },
2361 },
2362 'root' : {
2363 'level' : 'WARNING',
2364 },
2365 }
2366
2367 def apply_config(self, conf):
2368 logging.config.dictConfig(conf)
2369
2370 def test_config0_ok(self):
2371 # A simple config which overrides the default settings.
2372 with captured_stdout() as output:
2373 self.apply_config(self.config0)
2374 logger = logging.getLogger()
2375 # Won't output anything
2376 logger.info(self.next_message())
2377 # Outputs a message
2378 logger.error(self.next_message())
2379 self.assert_log_lines([
2380 ('ERROR', '2'),
2381 ], stream=output)
2382 # Original logger output is empty.
2383 self.assert_log_lines([])
2384
2385 def test_config1_ok(self, config=config1):
2386 # A config defining a sub-parser as well.
2387 with captured_stdout() as output:
2388 self.apply_config(config)
2389 logger = logging.getLogger("compiler.parser")
2390 # Both will output a message
2391 logger.info(self.next_message())
2392 logger.error(self.next_message())
2393 self.assert_log_lines([
2394 ('INFO', '1'),
2395 ('ERROR', '2'),
2396 ], stream=output)
2397 # Original logger output is empty.
2398 self.assert_log_lines([])
2399
2400 def test_config2_failure(self):
2401 # A simple config which overrides the default settings.
2402 self.assertRaises(Exception, self.apply_config, self.config2)
2403
2404 def test_config2a_failure(self):
2405 # A simple config which overrides the default settings.
2406 self.assertRaises(Exception, self.apply_config, self.config2a)
2407
2408 def test_config2b_failure(self):
2409 # A simple config which overrides the default settings.
2410 self.assertRaises(Exception, self.apply_config, self.config2b)
2411
2412 def test_config3_failure(self):
2413 # A simple config which overrides the default settings.
2414 self.assertRaises(Exception, self.apply_config, self.config3)
2415
2416 def test_config4_ok(self):
2417 # A config specifying a custom formatter class.
2418 with captured_stdout() as output:
2419 self.apply_config(self.config4)
2420 #logger = logging.getLogger()
2421 try:
2422 raise RuntimeError()
2423 except RuntimeError:
2424 logging.exception("just testing")
2425 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002426 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002427 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2428 # Original logger output is empty
2429 self.assert_log_lines([])
2430
2431 def test_config4a_ok(self):
2432 # A config specifying a custom formatter class.
2433 with captured_stdout() as output:
2434 self.apply_config(self.config4a)
2435 #logger = logging.getLogger()
2436 try:
2437 raise RuntimeError()
2438 except RuntimeError:
2439 logging.exception("just testing")
2440 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002441 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002442 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2443 # Original logger output is empty
2444 self.assert_log_lines([])
2445
2446 def test_config5_ok(self):
2447 self.test_config1_ok(config=self.config5)
2448
2449 def test_config6_failure(self):
2450 self.assertRaises(Exception, self.apply_config, self.config6)
2451
2452 def test_config7_ok(self):
2453 with captured_stdout() as output:
2454 self.apply_config(self.config1)
2455 logger = logging.getLogger("compiler.parser")
2456 # Both will output a message
2457 logger.info(self.next_message())
2458 logger.error(self.next_message())
2459 self.assert_log_lines([
2460 ('INFO', '1'),
2461 ('ERROR', '2'),
2462 ], stream=output)
2463 # Original logger output is empty.
2464 self.assert_log_lines([])
2465 with captured_stdout() as output:
2466 self.apply_config(self.config7)
2467 logger = logging.getLogger("compiler.parser")
2468 self.assertTrue(logger.disabled)
2469 logger = logging.getLogger("compiler.lexer")
2470 # Both will output a message
2471 logger.info(self.next_message())
2472 logger.error(self.next_message())
2473 self.assert_log_lines([
2474 ('INFO', '3'),
2475 ('ERROR', '4'),
2476 ], stream=output)
2477 # Original logger output is empty.
2478 self.assert_log_lines([])
2479
2480 #Same as test_config_7_ok but don't disable old loggers.
2481 def test_config_8_ok(self):
2482 with captured_stdout() as output:
2483 self.apply_config(self.config1)
2484 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002485 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002486 logger.info(self.next_message())
2487 logger.error(self.next_message())
2488 self.assert_log_lines([
2489 ('INFO', '1'),
2490 ('ERROR', '2'),
2491 ], stream=output)
2492 # Original logger output is empty.
2493 self.assert_log_lines([])
2494 with captured_stdout() as output:
2495 self.apply_config(self.config8)
2496 logger = logging.getLogger("compiler.parser")
2497 self.assertFalse(logger.disabled)
2498 # Both will output a message
2499 logger.info(self.next_message())
2500 logger.error(self.next_message())
2501 logger = logging.getLogger("compiler.lexer")
2502 # Both will output a message
2503 logger.info(self.next_message())
2504 logger.error(self.next_message())
2505 self.assert_log_lines([
2506 ('INFO', '3'),
2507 ('ERROR', '4'),
2508 ('INFO', '5'),
2509 ('ERROR', '6'),
2510 ], stream=output)
2511 # Original logger output is empty.
2512 self.assert_log_lines([])
2513
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002514 def test_config_8a_ok(self):
2515 with captured_stdout() as output:
2516 self.apply_config(self.config1a)
2517 logger = logging.getLogger("compiler.parser")
2518 # See issue #11424. compiler-hyphenated sorts
2519 # between compiler and compiler.xyz and this
2520 # was preventing compiler.xyz from being included
2521 # in the child loggers of compiler because of an
2522 # overzealous loop termination condition.
2523 hyphenated = logging.getLogger('compiler-hyphenated')
2524 # All will output a message
2525 logger.info(self.next_message())
2526 logger.error(self.next_message())
2527 hyphenated.critical(self.next_message())
2528 self.assert_log_lines([
2529 ('INFO', '1'),
2530 ('ERROR', '2'),
2531 ('CRITICAL', '3'),
2532 ], stream=output)
2533 # Original logger output is empty.
2534 self.assert_log_lines([])
2535 with captured_stdout() as output:
2536 self.apply_config(self.config8a)
2537 logger = logging.getLogger("compiler.parser")
2538 self.assertFalse(logger.disabled)
2539 # Both will output a message
2540 logger.info(self.next_message())
2541 logger.error(self.next_message())
2542 logger = logging.getLogger("compiler.lexer")
2543 # Both will output a message
2544 logger.info(self.next_message())
2545 logger.error(self.next_message())
2546 # Will not appear
2547 hyphenated.critical(self.next_message())
2548 self.assert_log_lines([
2549 ('INFO', '4'),
2550 ('ERROR', '5'),
2551 ('INFO', '6'),
2552 ('ERROR', '7'),
2553 ], stream=output)
2554 # Original logger output is empty.
2555 self.assert_log_lines([])
2556
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002557 def test_config_9_ok(self):
2558 with captured_stdout() as output:
2559 self.apply_config(self.config9)
2560 logger = logging.getLogger("compiler.parser")
2561 #Nothing will be output since both handler and logger are set to WARNING
2562 logger.info(self.next_message())
2563 self.assert_log_lines([], stream=output)
2564 self.apply_config(self.config9a)
2565 #Nothing will be output since both handler is still set to WARNING
2566 logger.info(self.next_message())
2567 self.assert_log_lines([], stream=output)
2568 self.apply_config(self.config9b)
2569 #Message should now be output
2570 logger.info(self.next_message())
2571 self.assert_log_lines([
2572 ('INFO', '3'),
2573 ], stream=output)
2574
2575 def test_config_10_ok(self):
2576 with captured_stdout() as output:
2577 self.apply_config(self.config10)
2578 logger = logging.getLogger("compiler.parser")
2579 logger.warning(self.next_message())
2580 logger = logging.getLogger('compiler')
2581 #Not output, because filtered
2582 logger.warning(self.next_message())
2583 logger = logging.getLogger('compiler.lexer')
2584 #Not output, because filtered
2585 logger.warning(self.next_message())
2586 logger = logging.getLogger("compiler.parser.codegen")
2587 #Output, as not filtered
2588 logger.error(self.next_message())
2589 self.assert_log_lines([
2590 ('WARNING', '1'),
2591 ('ERROR', '4'),
2592 ], stream=output)
2593
2594 def test_config11_ok(self):
2595 self.test_config1_ok(self.config11)
2596
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002597 def test_config12_failure(self):
2598 self.assertRaises(Exception, self.apply_config, self.config12)
2599
2600 def test_config13_failure(self):
2601 self.assertRaises(Exception, self.apply_config, self.config13)
2602
Victor Stinner45df8202010-04-28 22:31:17 +00002603 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002604 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002605 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002606 # Ask for a randomly assigned port (by using port 0)
2607 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002608 t.start()
2609 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002610 # Now get the port allocated
2611 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002612 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002613 try:
2614 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2615 sock.settimeout(2.0)
2616 sock.connect(('localhost', port))
2617
2618 slen = struct.pack('>L', len(text))
2619 s = slen + text
2620 sentsofar = 0
2621 left = len(s)
2622 while left > 0:
2623 sent = sock.send(s[sentsofar:])
2624 sentsofar += sent
2625 left -= sent
2626 sock.close()
2627 finally:
2628 t.ready.wait(2.0)
2629 logging.config.stopListening()
2630 t.join(2.0)
2631
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002632 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002633 def test_listen_config_10_ok(self):
2634 with captured_stdout() as output:
2635 self.setup_via_listener(json.dumps(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
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01002652 @unittest.skipUnless(threading, 'Threading required for this test.')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002653 def test_listen_config_1_ok(self):
2654 with captured_stdout() as output:
2655 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2656 logger = logging.getLogger("compiler.parser")
2657 # Both will output a message
2658 logger.info(self.next_message())
2659 logger.error(self.next_message())
2660 self.assert_log_lines([
2661 ('INFO', '1'),
2662 ('ERROR', '2'),
2663 ], stream=output)
2664 # Original logger output is empty.
2665 self.assert_log_lines([])
2666
Vinay Sajip373baef2011-04-26 20:05:24 +01002667 def test_baseconfig(self):
2668 d = {
2669 'atuple': (1, 2, 3),
2670 'alist': ['a', 'b', 'c'],
2671 'adict': {'d': 'e', 'f': 3 },
2672 'nest1': ('g', ('h', 'i'), 'j'),
2673 'nest2': ['k', ['l', 'm'], 'n'],
2674 'nest3': ['o', 'cfg://alist', 'p'],
2675 }
2676 bc = logging.config.BaseConfigurator(d)
2677 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2678 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2679 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2680 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2681 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2682 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2683 v = bc.convert('cfg://nest3')
2684 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2685 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2686 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2687 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002688
2689class ManagerTest(BaseTest):
2690 def test_manager_loggerclass(self):
2691 logged = []
2692
2693 class MyLogger(logging.Logger):
2694 def _log(self, level, msg, args, exc_info=None, extra=None):
2695 logged.append(msg)
2696
2697 man = logging.Manager(None)
2698 self.assertRaises(TypeError, man.setLoggerClass, int)
2699 man.setLoggerClass(MyLogger)
2700 logger = man.getLogger('test')
2701 logger.warning('should appear in logged')
2702 logging.warning('should not appear in logged')
2703
2704 self.assertEqual(logged, ['should appear in logged'])
2705
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002706 def test_set_log_record_factory(self):
2707 man = logging.Manager(None)
2708 expected = object()
2709 man.setLogRecordFactory(expected)
2710 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002711
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002712class ChildLoggerTest(BaseTest):
2713 def test_child_loggers(self):
2714 r = logging.getLogger()
2715 l1 = logging.getLogger('abc')
2716 l2 = logging.getLogger('def.ghi')
2717 c1 = r.getChild('xyz')
2718 c2 = r.getChild('uvw.xyz')
2719 self.assertTrue(c1 is logging.getLogger('xyz'))
2720 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2721 c1 = l1.getChild('def')
2722 c2 = c1.getChild('ghi')
2723 c3 = l1.getChild('def.ghi')
2724 self.assertTrue(c1 is logging.getLogger('abc.def'))
2725 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2726 self.assertTrue(c2 is c3)
2727
2728
Vinay Sajip6fac8172010-10-19 20:44:14 +00002729class DerivedLogRecord(logging.LogRecord):
2730 pass
2731
Vinay Sajip61561522010-12-03 11:50:38 +00002732class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002733
2734 def setUp(self):
2735 class CheckingFilter(logging.Filter):
2736 def __init__(self, cls):
2737 self.cls = cls
2738
2739 def filter(self, record):
2740 t = type(record)
2741 if t is not self.cls:
2742 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2743 self.cls)
2744 raise TypeError(msg)
2745 return True
2746
2747 BaseTest.setUp(self)
2748 self.filter = CheckingFilter(DerivedLogRecord)
2749 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002750 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002751
2752 def tearDown(self):
2753 self.root_logger.removeFilter(self.filter)
2754 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002755 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002756
2757 def test_logrecord_class(self):
2758 self.assertRaises(TypeError, self.root_logger.warning,
2759 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002760 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002761 self.root_logger.error(self.next_message())
2762 self.assert_log_lines([
2763 ('root', 'ERROR', '2'),
2764 ])
2765
2766
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002767class QueueHandlerTest(BaseTest):
2768 # Do not bother with a logger name group.
2769 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2770
2771 def setUp(self):
2772 BaseTest.setUp(self)
2773 self.queue = queue.Queue(-1)
2774 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2775 self.que_logger = logging.getLogger('que')
2776 self.que_logger.propagate = False
2777 self.que_logger.setLevel(logging.WARNING)
2778 self.que_logger.addHandler(self.que_hdlr)
2779
2780 def tearDown(self):
2781 self.que_hdlr.close()
2782 BaseTest.tearDown(self)
2783
2784 def test_queue_handler(self):
2785 self.que_logger.debug(self.next_message())
2786 self.assertRaises(queue.Empty, self.queue.get_nowait)
2787 self.que_logger.info(self.next_message())
2788 self.assertRaises(queue.Empty, self.queue.get_nowait)
2789 msg = self.next_message()
2790 self.que_logger.warning(msg)
2791 data = self.queue.get_nowait()
2792 self.assertTrue(isinstance(data, logging.LogRecord))
2793 self.assertEqual(data.name, self.que_logger.name)
2794 self.assertEqual((data.msg, data.args), (msg, None))
2795
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002796 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2797 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002798 def test_queue_listener(self):
2799 handler = TestHandler(Matcher())
2800 listener = logging.handlers.QueueListener(self.queue, handler)
2801 listener.start()
2802 try:
2803 self.que_logger.warning(self.next_message())
2804 self.que_logger.error(self.next_message())
2805 self.que_logger.critical(self.next_message())
2806 finally:
2807 listener.stop()
2808 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2809 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2810 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2811
Vinay Sajip37eb3382011-04-26 20:26:41 +01002812ZERO = datetime.timedelta(0)
2813
2814class UTC(datetime.tzinfo):
2815 def utcoffset(self, dt):
2816 return ZERO
2817
2818 dst = utcoffset
2819
2820 def tzname(self, dt):
2821 return 'UTC'
2822
2823utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002824
Vinay Sajipa39c5712010-10-25 13:57:39 +00002825class FormatterTest(unittest.TestCase):
2826 def setUp(self):
2827 self.common = {
2828 'name': 'formatter.test',
2829 'level': logging.DEBUG,
2830 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2831 'lineno': 42,
2832 'exc_info': None,
2833 'func': None,
2834 'msg': 'Message with %d %s',
2835 'args': (2, 'placeholders'),
2836 }
2837 self.variants = {
2838 }
2839
2840 def get_record(self, name=None):
2841 result = dict(self.common)
2842 if name is not None:
2843 result.update(self.variants[name])
2844 return logging.makeLogRecord(result)
2845
2846 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002847 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002848 r = self.get_record()
2849 f = logging.Formatter('${%(message)s}')
2850 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2851 f = logging.Formatter('%(random)s')
2852 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002853 self.assertFalse(f.usesTime())
2854 f = logging.Formatter('%(asctime)s')
2855 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002856 f = logging.Formatter('%(asctime)-15s')
2857 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002858 f = logging.Formatter('asctime')
2859 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002860
2861 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002862 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002863 r = self.get_record()
2864 f = logging.Formatter('$%{message}%$', style='{')
2865 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2866 f = logging.Formatter('{random}', style='{')
2867 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002868 self.assertFalse(f.usesTime())
2869 f = logging.Formatter('{asctime}', style='{')
2870 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002871 f = logging.Formatter('{asctime!s:15}', style='{')
2872 self.assertTrue(f.usesTime())
2873 f = logging.Formatter('{asctime:15}', style='{')
2874 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002875 f = logging.Formatter('asctime', style='{')
2876 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002877
2878 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002879 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002880 r = self.get_record()
2881 f = logging.Formatter('$message', style='$')
2882 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2883 f = logging.Formatter('$$%${message}%$$', style='$')
2884 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2885 f = logging.Formatter('${random}', style='$')
2886 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002887 self.assertFalse(f.usesTime())
2888 f = logging.Formatter('${asctime}', style='$')
2889 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002890 f = logging.Formatter('${asctime', style='$')
2891 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002892 f = logging.Formatter('$asctime', style='$')
2893 self.assertTrue(f.usesTime())
2894 f = logging.Formatter('asctime', style='$')
2895 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002896
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002897 def test_invalid_style(self):
2898 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2899
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002900 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002901 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01002902 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
2903 # We use None to indicate we want the local timezone
2904 # We're essentially converting a UTC time to local time
2905 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002906 r.msecs = 123
2907 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002908 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002909 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2910 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002911 f.format(r)
2912 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2913
2914class TestBufferingFormatter(logging.BufferingFormatter):
2915 def formatHeader(self, records):
2916 return '[(%d)' % len(records)
2917
2918 def formatFooter(self, records):
2919 return '(%d)]' % len(records)
2920
2921class BufferingFormatterTest(unittest.TestCase):
2922 def setUp(self):
2923 self.records = [
2924 logging.makeLogRecord({'msg': 'one'}),
2925 logging.makeLogRecord({'msg': 'two'}),
2926 ]
2927
2928 def test_default(self):
2929 f = logging.BufferingFormatter()
2930 self.assertEqual('', f.format([]))
2931 self.assertEqual('onetwo', f.format(self.records))
2932
2933 def test_custom(self):
2934 f = TestBufferingFormatter()
2935 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2936 lf = logging.Formatter('<%(message)s>')
2937 f = TestBufferingFormatter(lf)
2938 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002939
2940class ExceptionTest(BaseTest):
2941 def test_formatting(self):
2942 r = self.root_logger
2943 h = RecordingHandler()
2944 r.addHandler(h)
2945 try:
2946 raise RuntimeError('deliberate mistake')
2947 except:
2948 logging.exception('failed', stack_info=True)
2949 r.removeHandler(h)
2950 h.close()
2951 r = h.records[0]
2952 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2953 'call last):\n'))
2954 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2955 'deliberate mistake'))
2956 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2957 'call last):\n'))
2958 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2959 'stack_info=True)'))
2960
2961
Vinay Sajip5a27d402010-12-10 11:42:57 +00002962class LastResortTest(BaseTest):
2963 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002964 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002965 root = self.root_logger
2966 root.removeHandler(self.root_hdlr)
2967 old_stderr = sys.stderr
2968 old_lastresort = logging.lastResort
2969 old_raise_exceptions = logging.raiseExceptions
2970 try:
2971 sys.stderr = sio = io.StringIO()
Vinay Sajip8188f582011-07-25 19:58:13 +01002972 root.debug('This should not appear')
2973 self.assertEqual(sio.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00002974 root.warning('This is your final chance!')
2975 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2976 #No handlers and no last resort, so 'No handlers' message
2977 logging.lastResort = None
2978 sys.stderr = sio = io.StringIO()
2979 root.warning('This is your final chance!')
2980 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2981 # 'No handlers' message only printed once
2982 sys.stderr = sio = io.StringIO()
2983 root.warning('This is your final chance!')
2984 self.assertEqual(sio.getvalue(), '')
2985 root.manager.emittedNoHandlerWarning = False
2986 #If raiseExceptions is False, no message is printed
2987 logging.raiseExceptions = False
2988 sys.stderr = sio = io.StringIO()
2989 root.warning('This is your final chance!')
2990 self.assertEqual(sio.getvalue(), '')
2991 finally:
2992 sys.stderr = old_stderr
2993 root.addHandler(self.root_hdlr)
2994 logging.lastResort = old_lastresort
2995 logging.raiseExceptions = old_raise_exceptions
2996
2997
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002998class FakeHandler:
2999
3000 def __init__(self, identifier, called):
3001 for method in ('acquire', 'flush', 'close', 'release'):
3002 setattr(self, method, self.record_call(identifier, method, called))
3003
3004 def record_call(self, identifier, method_name, called):
3005 def inner():
3006 called.append('{} - {}'.format(identifier, method_name))
3007 return inner
3008
3009
3010class RecordingHandler(logging.NullHandler):
3011
3012 def __init__(self, *args, **kwargs):
3013 super(RecordingHandler, self).__init__(*args, **kwargs)
3014 self.records = []
3015
3016 def handle(self, record):
3017 """Keep track of all the emitted records."""
3018 self.records.append(record)
3019
3020
3021class ShutdownTest(BaseTest):
3022
Vinay Sajip5e66b162011-04-20 15:41:14 +01003023 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003024
3025 def setUp(self):
3026 super(ShutdownTest, self).setUp()
3027 self.called = []
3028
3029 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003030 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003031
3032 def raise_error(self, error):
3033 def inner():
3034 raise error()
3035 return inner
3036
3037 def test_no_failure(self):
3038 # create some fake handlers
3039 handler0 = FakeHandler(0, self.called)
3040 handler1 = FakeHandler(1, self.called)
3041 handler2 = FakeHandler(2, self.called)
3042
3043 # create live weakref to those handlers
3044 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
3045
3046 logging.shutdown(handlerList=list(handlers))
3047
3048 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
3049 '1 - acquire', '1 - flush', '1 - close', '1 - release',
3050 '0 - acquire', '0 - flush', '0 - close', '0 - release']
3051 self.assertEqual(expected, self.called)
3052
3053 def _test_with_failure_in_method(self, method, error):
3054 handler = FakeHandler(0, self.called)
3055 setattr(handler, method, self.raise_error(error))
3056 handlers = [logging.weakref.ref(handler)]
3057
3058 logging.shutdown(handlerList=list(handlers))
3059
3060 self.assertEqual('0 - release', self.called[-1])
3061
3062 def test_with_ioerror_in_acquire(self):
3063 self._test_with_failure_in_method('acquire', IOError)
3064
3065 def test_with_ioerror_in_flush(self):
3066 self._test_with_failure_in_method('flush', IOError)
3067
3068 def test_with_ioerror_in_close(self):
3069 self._test_with_failure_in_method('close', IOError)
3070
3071 def test_with_valueerror_in_acquire(self):
3072 self._test_with_failure_in_method('acquire', ValueError)
3073
3074 def test_with_valueerror_in_flush(self):
3075 self._test_with_failure_in_method('flush', ValueError)
3076
3077 def test_with_valueerror_in_close(self):
3078 self._test_with_failure_in_method('close', ValueError)
3079
3080 def test_with_other_error_in_acquire_without_raise(self):
3081 logging.raiseExceptions = False
3082 self._test_with_failure_in_method('acquire', IndexError)
3083
3084 def test_with_other_error_in_flush_without_raise(self):
3085 logging.raiseExceptions = False
3086 self._test_with_failure_in_method('flush', IndexError)
3087
3088 def test_with_other_error_in_close_without_raise(self):
3089 logging.raiseExceptions = False
3090 self._test_with_failure_in_method('close', IndexError)
3091
3092 def test_with_other_error_in_acquire_with_raise(self):
3093 logging.raiseExceptions = True
3094 self.assertRaises(IndexError, self._test_with_failure_in_method,
3095 'acquire', IndexError)
3096
3097 def test_with_other_error_in_flush_with_raise(self):
3098 logging.raiseExceptions = True
3099 self.assertRaises(IndexError, self._test_with_failure_in_method,
3100 'flush', IndexError)
3101
3102 def test_with_other_error_in_close_with_raise(self):
3103 logging.raiseExceptions = True
3104 self.assertRaises(IndexError, self._test_with_failure_in_method,
3105 'close', IndexError)
3106
3107
3108class ModuleLevelMiscTest(BaseTest):
3109
Vinay Sajip5e66b162011-04-20 15:41:14 +01003110 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003111
3112 def test_disable(self):
3113 old_disable = logging.root.manager.disable
3114 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003115 self.assertEqual(old_disable, 0)
3116 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003117
3118 logging.disable(83)
3119 self.assertEqual(logging.root.manager.disable, 83)
3120
3121 def _test_log(self, method, level=None):
3122 called = []
3123 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01003124 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003125
3126 recording = RecordingHandler()
3127 logging.root.addHandler(recording)
3128
3129 log_method = getattr(logging, method)
3130 if level is not None:
3131 log_method(level, "test me: %r", recording)
3132 else:
3133 log_method("test me: %r", recording)
3134
3135 self.assertEqual(len(recording.records), 1)
3136 record = recording.records[0]
3137 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3138
3139 expected_level = level if level is not None else getattr(logging, method.upper())
3140 self.assertEqual(record.levelno, expected_level)
3141
3142 # basicConfig was not called!
3143 self.assertEqual(called, [])
3144
3145 def test_log(self):
3146 self._test_log('log', logging.ERROR)
3147
3148 def test_debug(self):
3149 self._test_log('debug')
3150
3151 def test_info(self):
3152 self._test_log('info')
3153
3154 def test_warning(self):
3155 self._test_log('warning')
3156
3157 def test_error(self):
3158 self._test_log('error')
3159
3160 def test_critical(self):
3161 self._test_log('critical')
3162
3163 def test_set_logger_class(self):
3164 self.assertRaises(TypeError, logging.setLoggerClass, object)
3165
3166 class MyLogger(logging.Logger):
3167 pass
3168
3169 logging.setLoggerClass(MyLogger)
3170 self.assertEqual(logging.getLoggerClass(), MyLogger)
3171
3172 logging.setLoggerClass(logging.Logger)
3173 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3174
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003175class LogRecordTest(BaseTest):
3176 def test_str_rep(self):
3177 r = logging.makeLogRecord({})
3178 s = str(r)
3179 self.assertTrue(s.startswith('<LogRecord: '))
3180 self.assertTrue(s.endswith('>'))
3181
3182 def test_dict_arg(self):
3183 h = RecordingHandler()
3184 r = logging.getLogger()
3185 r.addHandler(h)
3186 d = {'less' : 'more' }
3187 logging.warning('less is %(less)s', d)
3188 self.assertIs(h.records[0].args, d)
3189 self.assertEqual(h.records[0].message, 'less is more')
3190 r.removeHandler(h)
3191 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003192
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003193 def test_multiprocessing(self):
3194 r = logging.makeLogRecord({})
3195 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003196 try:
3197 import multiprocessing as mp
3198 r = logging.makeLogRecord({})
3199 self.assertEqual(r.processName, mp.current_process().name)
3200 except ImportError:
3201 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003202
3203 def test_optional(self):
3204 r = logging.makeLogRecord({})
3205 NOT_NONE = self.assertIsNotNone
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01003206 if threading:
3207 NOT_NONE(r.thread)
3208 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003209 NOT_NONE(r.process)
3210 NOT_NONE(r.processName)
3211 log_threads = logging.logThreads
3212 log_processes = logging.logProcesses
3213 log_multiprocessing = logging.logMultiprocessing
3214 try:
3215 logging.logThreads = False
3216 logging.logProcesses = False
3217 logging.logMultiprocessing = False
3218 r = logging.makeLogRecord({})
3219 NONE = self.assertIsNone
3220 NONE(r.thread)
3221 NONE(r.threadName)
3222 NONE(r.process)
3223 NONE(r.processName)
3224 finally:
3225 logging.logThreads = log_threads
3226 logging.logProcesses = log_processes
3227 logging.logMultiprocessing = log_multiprocessing
3228
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003229class BasicConfigTest(unittest.TestCase):
3230
Vinay Sajip95bf5042011-04-20 11:50:56 +01003231 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003232
3233 def setUp(self):
3234 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003235 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003236 self.saved_handlers = logging._handlers.copy()
3237 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003238 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003239 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003240 logging.root.handlers = []
3241
3242 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003243 for h in logging.root.handlers[:]:
3244 logging.root.removeHandler(h)
3245 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003246 super(BasicConfigTest, self).tearDown()
3247
Vinay Sajip3def7e02011-04-20 10:58:06 +01003248 def cleanup(self):
3249 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003250 logging._handlers.clear()
3251 logging._handlers.update(self.saved_handlers)
3252 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003253 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003254
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003255 def test_no_kwargs(self):
3256 logging.basicConfig()
3257
3258 # handler defaults to a StreamHandler to sys.stderr
3259 self.assertEqual(len(logging.root.handlers), 1)
3260 handler = logging.root.handlers[0]
3261 self.assertIsInstance(handler, logging.StreamHandler)
3262 self.assertEqual(handler.stream, sys.stderr)
3263
3264 formatter = handler.formatter
3265 # format defaults to logging.BASIC_FORMAT
3266 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3267 # datefmt defaults to None
3268 self.assertIsNone(formatter.datefmt)
3269 # style defaults to %
3270 self.assertIsInstance(formatter._style, logging.PercentStyle)
3271
Florent Xiclunae41f0de2011-11-11 19:39:25 +01003272 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003273 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003274
3275 def test_filename(self):
3276 logging.basicConfig(filename='test.log')
3277
3278 self.assertEqual(len(logging.root.handlers), 1)
3279 handler = logging.root.handlers[0]
3280 self.assertIsInstance(handler, logging.FileHandler)
3281
3282 expected = logging.FileHandler('test.log', 'a')
3283 self.addCleanup(expected.close)
3284 self.assertEqual(handler.stream.mode, expected.stream.mode)
3285 self.assertEqual(handler.stream.name, expected.stream.name)
3286
3287 def test_filemode(self):
3288 logging.basicConfig(filename='test.log', filemode='wb')
3289
3290 handler = logging.root.handlers[0]
3291 expected = logging.FileHandler('test.log', 'wb')
3292 self.addCleanup(expected.close)
3293 self.assertEqual(handler.stream.mode, expected.stream.mode)
3294
3295 def test_stream(self):
3296 stream = io.StringIO()
3297 self.addCleanup(stream.close)
3298 logging.basicConfig(stream=stream)
3299
3300 self.assertEqual(len(logging.root.handlers), 1)
3301 handler = logging.root.handlers[0]
3302 self.assertIsInstance(handler, logging.StreamHandler)
3303 self.assertEqual(handler.stream, stream)
3304
3305 def test_format(self):
3306 logging.basicConfig(format='foo')
3307
3308 formatter = logging.root.handlers[0].formatter
3309 self.assertEqual(formatter._style._fmt, 'foo')
3310
3311 def test_datefmt(self):
3312 logging.basicConfig(datefmt='bar')
3313
3314 formatter = logging.root.handlers[0].formatter
3315 self.assertEqual(formatter.datefmt, 'bar')
3316
3317 def test_style(self):
3318 logging.basicConfig(style='$')
3319
3320 formatter = logging.root.handlers[0].formatter
3321 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3322
3323 def test_level(self):
3324 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003325 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003326
3327 logging.basicConfig(level=57)
3328 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003329 # Test that second call has no effect
3330 logging.basicConfig(level=58)
3331 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003332
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003333 def test_incompatible(self):
3334 assertRaises = self.assertRaises
3335 handlers = [logging.StreamHandler()]
3336 stream = sys.stderr
3337 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3338 stream=stream)
3339 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3340 handlers=handlers)
3341 assertRaises(ValueError, logging.basicConfig, stream=stream,
3342 handlers=handlers)
3343
3344 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003345 handlers = [
3346 logging.StreamHandler(),
3347 logging.StreamHandler(sys.stdout),
3348 logging.StreamHandler(),
3349 ]
3350 f = logging.Formatter()
3351 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003352 logging.basicConfig(handlers=handlers)
3353 self.assertIs(handlers[0], logging.root.handlers[0])
3354 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003355 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003356 self.assertIsNotNone(handlers[0].formatter)
3357 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003358 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003359 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3360
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003361 def _test_log(self, method, level=None):
3362 # logging.root has no handlers so basicConfig should be called
3363 called = []
3364
3365 old_basic_config = logging.basicConfig
3366 def my_basic_config(*a, **kw):
3367 old_basic_config()
3368 old_level = logging.root.level
3369 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003370 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003371 called.append((a, kw))
3372
3373 patch(self, logging, 'basicConfig', my_basic_config)
3374
3375 log_method = getattr(logging, method)
3376 if level is not None:
3377 log_method(level, "test me")
3378 else:
3379 log_method("test me")
3380
3381 # basicConfig was called with no arguments
3382 self.assertEqual(called, [((), {})])
3383
3384 def test_log(self):
3385 self._test_log('log', logging.WARNING)
3386
3387 def test_debug(self):
3388 self._test_log('debug')
3389
3390 def test_info(self):
3391 self._test_log('info')
3392
3393 def test_warning(self):
3394 self._test_log('warning')
3395
3396 def test_error(self):
3397 self._test_log('error')
3398
3399 def test_critical(self):
3400 self._test_log('critical')
3401
3402
3403class LoggerAdapterTest(unittest.TestCase):
3404
3405 def setUp(self):
3406 super(LoggerAdapterTest, self).setUp()
3407 old_handler_list = logging._handlerList[:]
3408
3409 self.recording = RecordingHandler()
3410 self.logger = logging.root
3411 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003412 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003413 self.addCleanup(self.recording.close)
3414
3415 def cleanup():
3416 logging._handlerList[:] = old_handler_list
3417
3418 self.addCleanup(cleanup)
3419 self.addCleanup(logging.shutdown)
3420 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3421
3422 def test_exception(self):
3423 msg = 'testing exception: %r'
3424 exc = None
3425 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003426 1 / 0
3427 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003428 exc = e
3429 self.adapter.exception(msg, self.recording)
3430
3431 self.assertEqual(len(self.recording.records), 1)
3432 record = self.recording.records[0]
3433 self.assertEqual(record.levelno, logging.ERROR)
3434 self.assertEqual(record.msg, msg)
3435 self.assertEqual(record.args, (self.recording,))
3436 self.assertEqual(record.exc_info,
3437 (exc.__class__, exc, exc.__traceback__))
3438
3439 def test_critical(self):
3440 msg = 'critical test! %r'
3441 self.adapter.critical(msg, self.recording)
3442
3443 self.assertEqual(len(self.recording.records), 1)
3444 record = self.recording.records[0]
3445 self.assertEqual(record.levelno, logging.CRITICAL)
3446 self.assertEqual(record.msg, msg)
3447 self.assertEqual(record.args, (self.recording,))
3448
3449 def test_is_enabled_for(self):
3450 old_disable = self.adapter.logger.manager.disable
3451 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003452 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
3453 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003454 self.assertFalse(self.adapter.isEnabledFor(32))
3455
3456 def test_has_handlers(self):
3457 self.assertTrue(self.adapter.hasHandlers())
3458
3459 for handler in self.logger.handlers:
3460 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003461
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003462 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003463 self.assertFalse(self.adapter.hasHandlers())
3464
3465
3466class LoggerTest(BaseTest):
3467
3468 def setUp(self):
3469 super(LoggerTest, self).setUp()
3470 self.recording = RecordingHandler()
3471 self.logger = logging.Logger(name='blah')
3472 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003473 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003474 self.addCleanup(self.recording.close)
3475 self.addCleanup(logging.shutdown)
3476
3477 def test_set_invalid_level(self):
3478 self.assertRaises(TypeError, self.logger.setLevel, object())
3479
3480 def test_exception(self):
3481 msg = 'testing exception: %r'
3482 exc = None
3483 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003484 1 / 0
3485 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003486 exc = e
3487 self.logger.exception(msg, self.recording)
3488
3489 self.assertEqual(len(self.recording.records), 1)
3490 record = self.recording.records[0]
3491 self.assertEqual(record.levelno, logging.ERROR)
3492 self.assertEqual(record.msg, msg)
3493 self.assertEqual(record.args, (self.recording,))
3494 self.assertEqual(record.exc_info,
3495 (exc.__class__, exc, exc.__traceback__))
3496
3497 def test_log_invalid_level_with_raise(self):
3498 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003499 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003500
3501 logging.raiseExceptions = True
3502 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3503
3504 def test_log_invalid_level_no_raise(self):
3505 old_raise = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003506 self.addCleanup(setattr, logging, 'raiseExecptions', old_raise)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003507
3508 logging.raiseExceptions = False
3509 self.logger.log('10', 'test message') # no exception happens
3510
3511 def test_find_caller_with_stack_info(self):
3512 called = []
3513 patch(self, logging.traceback, 'print_stack',
3514 lambda f, file: called.append(file.getvalue()))
3515
3516 self.logger.findCaller(stack_info=True)
3517
3518 self.assertEqual(len(called), 1)
3519 self.assertEqual('Stack (most recent call last):\n', called[0])
3520
3521 def test_make_record_with_extra_overwrite(self):
3522 name = 'my record'
3523 level = 13
3524 fn = lno = msg = args = exc_info = func = sinfo = None
3525 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3526 exc_info, func, sinfo)
3527
3528 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3529 extra = {key: 'some value'}
3530 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3531 fn, lno, msg, args, exc_info,
3532 extra=extra, sinfo=sinfo)
3533
3534 def test_make_record_with_extra_no_overwrite(self):
3535 name = 'my record'
3536 level = 13
3537 fn = lno = msg = args = exc_info = func = sinfo = None
3538 extra = {'valid_key': 'some value'}
3539 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3540 exc_info, extra=extra, sinfo=sinfo)
3541 self.assertIn('valid_key', result.__dict__)
3542
3543 def test_has_handlers(self):
3544 self.assertTrue(self.logger.hasHandlers())
3545
3546 for handler in self.logger.handlers:
3547 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003548 self.assertFalse(self.logger.hasHandlers())
3549
3550 def test_has_handlers_no_propagate(self):
3551 child_logger = logging.getLogger('blah.child')
3552 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003553 self.assertFalse(child_logger.hasHandlers())
3554
3555 def test_is_enabled_for(self):
3556 old_disable = self.logger.manager.disable
3557 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01003558 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003559 self.assertFalse(self.logger.isEnabledFor(22))
3560
Florent Xicluna5252f9f2011-11-07 19:43:05 +01003561 def test_root_logger_aliases(self):
3562 root = logging.getLogger()
3563 self.assertIs(root, logging.root)
3564 self.assertIs(root, logging.getLogger(None))
3565 self.assertIs(root, logging.getLogger(''))
3566 self.assertIs(root, logging.getLogger('foo').root)
3567 self.assertIs(root, logging.getLogger('foo.bar').root)
3568 self.assertIs(root, logging.getLogger('foo').parent)
3569
3570 self.assertIsNot(root, logging.getLogger('\0'))
3571 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
3572
3573 def test_invalid_names(self):
3574 self.assertRaises(TypeError, logging.getLogger, any)
3575 self.assertRaises(TypeError, logging.getLogger, b'foo')
3576
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003577
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003578class BaseFileTest(BaseTest):
3579 "Base class for handler tests that write log files"
3580
3581 def setUp(self):
3582 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003583 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3584 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003585 self.rmfiles = []
3586
3587 def tearDown(self):
3588 for fn in self.rmfiles:
3589 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003590 if os.path.exists(self.fn):
3591 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003592 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003593
3594 def assertLogFile(self, filename):
3595 "Assert a log file is there and register it for deletion"
3596 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003597 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003598 self.rmfiles.append(filename)
3599
3600
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003601class FileHandlerTest(BaseFileTest):
3602 def test_delay(self):
3603 os.unlink(self.fn)
3604 fh = logging.FileHandler(self.fn, delay=True)
3605 self.assertIsNone(fh.stream)
3606 self.assertFalse(os.path.exists(self.fn))
3607 fh.handle(logging.makeLogRecord({}))
3608 self.assertIsNotNone(fh.stream)
3609 self.assertTrue(os.path.exists(self.fn))
3610 fh.close()
3611
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003612class RotatingFileHandlerTest(BaseFileTest):
3613 def next_rec(self):
3614 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3615 self.next_message(), None, None, None)
3616
3617 def test_should_not_rollover(self):
3618 # If maxbytes is zero rollover never occurs
3619 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3620 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003621 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003622
3623 def test_should_rollover(self):
3624 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3625 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003626 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003627
3628 def test_file_created(self):
3629 # checks that the file is created and assumes it was created
3630 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003631 rh = logging.handlers.RotatingFileHandler(self.fn)
3632 rh.emit(self.next_rec())
3633 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003634 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003635
3636 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00003637 def namer(name):
3638 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003639 rh = logging.handlers.RotatingFileHandler(
3640 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003641 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003642 rh.emit(self.next_rec())
3643 self.assertLogFile(self.fn)
3644 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003645 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003646 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00003647 self.assertLogFile(namer(self.fn + ".2"))
3648 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
3649 rh.close()
3650
Eric V. Smith851cad72012-03-11 22:46:04 -07003651 @requires_zlib
Vinay Sajip23b94d02012-01-04 12:02:26 +00003652 def test_rotator(self):
3653 def namer(name):
3654 return name + ".gz"
3655
3656 def rotator(source, dest):
3657 with open(source, "rb") as sf:
3658 data = sf.read()
3659 compressed = zlib.compress(data, 9)
3660 with open(dest, "wb") as df:
3661 df.write(compressed)
3662 os.remove(source)
3663
3664 rh = logging.handlers.RotatingFileHandler(
3665 self.fn, backupCount=2, maxBytes=1)
3666 rh.rotator = rotator
3667 rh.namer = namer
3668 m1 = self.next_rec()
3669 rh.emit(m1)
3670 self.assertLogFile(self.fn)
3671 m2 = self.next_rec()
3672 rh.emit(m2)
3673 fn = namer(self.fn + ".1")
3674 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003675 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00003676 with open(fn, "rb") as f:
3677 compressed = f.read()
3678 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003679 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003680 rh.emit(self.next_rec())
3681 fn = namer(self.fn + ".2")
3682 self.assertLogFile(fn)
3683 with open(fn, "rb") as f:
3684 compressed = f.read()
3685 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003686 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003687 rh.emit(self.next_rec())
3688 fn = namer(self.fn + ".2")
3689 with open(fn, "rb") as f:
3690 compressed = f.read()
3691 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00003692 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00003693 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00003694 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003695
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003696class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003697 # other test methods added below
3698 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003699 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3700 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00003701 fmt = logging.Formatter('%(asctime)s %(message)s')
3702 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003703 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003704 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003705 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00003706 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00003707 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00003708 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01003709 fh.close()
3710 # At this point, we should have a recent rotated file which we
3711 # can test for the existence of. However, in practice, on some
3712 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01003713 # in time to go to look for the log file. So, we go back a fair
3714 # bit, and stop as soon as we see a rotated file. In theory this
3715 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01003716 found = False
3717 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01003718 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01003719 for secs in range(GO_BACK):
3720 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01003721 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3722 found = os.path.exists(fn)
3723 if found:
3724 self.rmfiles.append(fn)
3725 break
Vinay Sajip672c5812011-05-13 07:09:40 +01003726 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3727 if not found:
3728 #print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01003729 dn, fn = os.path.split(self.fn)
3730 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02003731 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
3732 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00003733 for f in files:
3734 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00003735 path = os.path.join(dn, f)
3736 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00003737 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01003738 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003739
Vinay Sajip0372e102011-05-05 12:59:14 +01003740 def test_invalid(self):
3741 assertRaises = self.assertRaises
3742 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003743 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003744 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003745 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003746 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003747 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003748
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003749def secs(**kw):
3750 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3751
3752for when, exp in (('S', 1),
3753 ('M', 60),
3754 ('H', 60 * 60),
3755 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003756 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003757 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003758 ('W0', secs(days=4, hours=24)),
3759 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003760 def test_compute_rollover(self, when=when, exp=exp):
3761 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003762 self.fn, when=when, interval=1, backupCount=0, utc=True)
3763 currentTime = 0.0
3764 actual = rh.computeRollover(currentTime)
3765 if exp != actual:
3766 # Failures occur on some systems for MIDNIGHT and W0.
3767 # Print detailed calculation for MIDNIGHT so we can try to see
3768 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00003769 if when == 'MIDNIGHT':
3770 try:
3771 if rh.utc:
3772 t = time.gmtime(currentTime)
3773 else:
3774 t = time.localtime(currentTime)
3775 currentHour = t[3]
3776 currentMinute = t[4]
3777 currentSecond = t[5]
3778 # r is the number of seconds left between now and midnight
3779 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3780 currentMinute) * 60 +
3781 currentSecond)
3782 result = currentTime + r
3783 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3784 print('currentHour: %s' % currentHour, file=sys.stderr)
3785 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3786 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3787 print('r: %s' % r, file=sys.stderr)
3788 print('result: %s' % result, file=sys.stderr)
3789 except Exception:
3790 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3791 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003792 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003793 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3794
Vinay Sajip60ccd822011-05-09 17:32:09 +01003795
3796@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3797class NTEventLogHandlerTest(BaseTest):
3798 def test_basic(self):
3799 logtype = 'Application'
3800 elh = win32evtlog.OpenEventLog(None, logtype)
3801 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3802 h = logging.handlers.NTEventLogHandler('test_logging')
3803 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3804 h.handle(r)
3805 h.close()
3806 # Now see if the event is recorded
3807 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3808 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3809 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3810 found = False
3811 GO_BACK = 100
3812 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3813 for e in events:
3814 if e.SourceName != 'test_logging':
3815 continue
3816 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3817 if msg != 'Test Log Message\r\n':
3818 continue
3819 found = True
3820 break
3821 msg = 'Record not found in event log, went back %d records' % GO_BACK
3822 self.assertTrue(found, msg=msg)
3823
Christian Heimes180510d2008-03-03 19:15:45 +00003824# Set the locale to the platform-dependent default. I have no idea
3825# why the test does this, but in any case we save the current locale
3826# first and restore it at the end.
3827@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003828def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003829 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003830 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003831 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3832 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3833 ManagerTest, FormatterTest, BufferingFormatterTest,
3834 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3835 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3836 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3837 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003838 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003839 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003840 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003841 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003842
Christian Heimes180510d2008-03-03 19:15:45 +00003843if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003844 test_main()