diff -r b459883e6fe7 Lib/test/test_logging.py --- a/Lib/test/test_logging.py Sat Feb 23 21:40:35 2008 +0100 +++ b/Lib/test/test_logging.py Sat Feb 23 23:19:53 2008 +0100 @@ -1,1895 +1,705 @@ #!/usr/bin/env python +# +# Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# This file is part of the Python logging distribution. See +# http://www.red-dove.com/python_logging.html +# +"""Test harness for the logging module. Run all tests. + +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. """ -Test 0 -====== -Some preliminaries: ->>> import sys ->>> import logging ->>> def nextmessage(): -... global msgcount -... rv = "Message %d" % msgcount -... msgcount = msgcount + 1 -... return rv +import select +import unittest +import os, sys, string, struct, types, cPickle, cStringIO, re +import socket, tempfile, threading, time +import weakref, gc, copy +import textwrap +import logging, logging.handlers, logging.config +from SocketServer import ThreadingTCPServer, StreamRequestHandler -Set a few variables, then go through the logger autoconfig and set the default threshold. ->>> msgcount = 0 ->>> FINISH_UP = "Finish up, it's closing time. Messages should bear numbers 0 through 24." ->>> logging.basicConfig(stream=sys.stdout) ->>> rootLogger = logging.getLogger("") ->>> rootLogger.setLevel(logging.DEBUG) +from test.test_support import run_with_locale, run_unittest -Now, create a bunch of loggers, and set their thresholds. ->>> ERR = logging.getLogger("ERR0") ->>> ERR.setLevel(logging.ERROR) ->>> INF = logging.getLogger("INFO0") ->>> INF.setLevel(logging.INFO) ->>> INF_ERR = logging.getLogger("INFO0.ERR") ->>> INF_ERR.setLevel(logging.ERROR) ->>> DEB = logging.getLogger("DEB0") ->>> DEB.setLevel(logging.DEBUG) ->>> INF_UNDEF = logging.getLogger("INFO0.UNDEF") ->>> INF_ERR_UNDEF = logging.getLogger("INFO0.ERR.UNDEF") ->>> UNDEF = logging.getLogger("UNDEF0") ->>> GRANDCHILD = logging.getLogger("INFO0.BADPARENT.UNDEF") ->>> CHILD = logging.getLogger("INFO0.BADPARENT") +tests = [] # list to accumulate all tests -And finally, run all the tests. +class BaseTest(unittest.TestCase): + """ + Base class for logging tests. + """ ->>> ERR.log(logging.FATAL, nextmessage()) -CRITICAL:ERR0:Message 0 + log_format = "%(name)s -> %(levelname)s: %(message)s" + expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$" + msgnum = 0 ->>> ERR.error(nextmessage()) -ERROR:ERR0:Message 1 + def setUp(self): + """ + Setup the default logging stream to an internal StringIO instance, so + that we can examine log output as we want. + """ + loggerDict = logging.getLogger().manager.loggerDict + logging._acquireLock() + try: + self.saved_handlers = logging._handlers.copy() + self.saved_handler_list = logging._handlerList[:] + self.saved_loggers = loggerDict.copy() + self.saved_level_names = logging._levelNames.copy() + finally: + logging._releaseLock() ->>> INF.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0:Message 2 + self.root_logger = logging.getLogger("") + self.original_logging_level = self.root_logger.getEffectiveLevel() ->>> INF.error(nextmessage()) -ERROR:INFO0:Message 3 + self.stream = cStringIO.StringIO() + self.root_logger.setLevel(logging.DEBUG) + self.root_hdlr = logging.StreamHandler(self.stream) + self.root_formatter = logging.Formatter(self.log_format) + self.root_hdlr.setFormatter(self.root_formatter) + self.root_logger.addHandler(self.root_hdlr) ->>> INF.warn(nextmessage()) -WARNING:INFO0:Message 4 + def tearDown(self): + """ + Remove our logging stream, and restore the original logging level. + """ + self.stream.close() + self.root_logger.removeHandler(self.root_hdlr) + self.root_logger.setLevel(self.original_logging_level) + logging._acquireLock() + try: + logging._levelNames.clear() + logging._levelNames.update(self.saved_level_names) + logging._handlers.clear() + logging._handlers.update(self.saved_handlers) + logging._handlerList[:] = self.saved_handler_list + loggerDict = logging.getLogger().manager.loggerDict + loggerDict.clear() + loggerDict.update(self.saved_loggers) + finally: + logging._releaseLock() ->>> INF.info(nextmessage()) -INFO:INFO0:Message 5 + def assertLogLines(self, expected_values, stream=None): + """ + Match the collected log lines against the regular expression + self.expected_log_pat, and compare the extracted group values to + the expected_values list of tuples. + """ + stream = stream or self.stream + pat = re.compile(self.expected_log_pat) + stream.reset() + actual_lines = stream.readlines() + self.assertEquals(len(actual_lines), len(expected_values)) + for actual, expected in zip(actual_lines, expected_values): + match = pat.search(actual) + if not match: + self.fail("Log line does not match expected pattern:\n" + actual) + self.assertEquals(tuple(match.groups()), expected) + s = stream.read() + if s: + self.fail("Remaining output at end of log stream:\n" + s) ->>> INF_UNDEF.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.UNDEF:Message 6 + def next_message(self): + """ + Generate a message consisting solely of an auto-incrementing integer. + """ + self.msgnum += 1 + return "%d" % self.msgnum ->>> INF_UNDEF.error(nextmessage()) -ERROR:INFO0.UNDEF:Message 7 ->>> INF_UNDEF.warn (nextmessage()) -WARNING:INFO0.UNDEF:Message 8 +class BuiltinLevelsTest(BaseTest): + """ + Test builtin levels and their inheritance. + """ ->>> INF_UNDEF.info (nextmessage()) -INFO:INFO0.UNDEF:Message 9 + def test_flat(self): + """ + Logging levels in a flat logger namespace. + """ + m = self.next_message ->>> INF_ERR.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.ERR:Message 10 + ERR = logging.getLogger("ERR") + ERR.setLevel(logging.ERROR) + INF = logging.getLogger("INF") + INF.setLevel(logging.INFO) + DEB = logging.getLogger("DEB") + DEB.setLevel(logging.DEBUG) ->>> INF_ERR.error(nextmessage()) -ERROR:INFO0.ERR:Message 11 + # These should log + ERR.log(logging.FATAL, m()) + ERR.error(m()) ->>> INF_ERR_UNDEF.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.ERR.UNDEF:Message 12 + INF.log(logging.FATAL, m()) + INF.error(m()) + INF.warn(m()) + INF.info(m()) ->>> INF_ERR_UNDEF.error(nextmessage()) -ERROR:INFO0.ERR.UNDEF:Message 13 + DEB.log(logging.FATAL, m()) + DEB.error(m()) + DEB.warn (m()) + DEB.info (m()) + DEB.debug(m()) ->>> DEB.log(logging.FATAL, nextmessage()) -CRITICAL:DEB0:Message 14 + # These should not log + ERR.warn(m()) + ERR.info(m()) + ERR.debug(m()) ->>> DEB.error(nextmessage()) -ERROR:DEB0:Message 15 + INF.debug(m()) ->>> DEB.warn (nextmessage()) -WARNING:DEB0:Message 16 + self.assertLogLines([ + ('ERR', 'CRITICAL', '1'), + ('ERR', 'ERROR', '2'), + ('INF', 'CRITICAL', '3'), + ('INF', 'ERROR', '4'), + ('INF', 'WARNING', '5'), + ('INF', 'INFO', '6'), + ('DEB', 'CRITICAL', '7'), + ('DEB', 'ERROR', '8'), + ('DEB', 'WARNING', '9'), + ('DEB', 'INFO', '10'), + ('DEB', 'DEBUG', '11'), + ]) ->>> DEB.info (nextmessage()) -INFO:DEB0:Message 17 + def test_nested_explicit(self): + """ + Logging levels in a nested namespace, all explicitly set. + """ + m = self.next_message ->>> DEB.debug(nextmessage()) -DEBUG:DEB0:Message 18 + INF = logging.getLogger("INF") + INF.setLevel(logging.INFO) + INF_ERR = logging.getLogger("INF.ERR") + INF_ERR.setLevel(logging.ERROR) ->>> UNDEF.log(logging.FATAL, nextmessage()) -CRITICAL:UNDEF0:Message 19 + # These should log + INF_ERR.log(logging.FATAL, m()) + INF_ERR.error(m()) ->>> UNDEF.error(nextmessage()) -ERROR:UNDEF0:Message 20 + # These should not log + INF_ERR.warn(m()) + INF_ERR.info(m()) + INF_ERR.debug(m()) ->>> UNDEF.warn (nextmessage()) -WARNING:UNDEF0:Message 21 + self.assertLogLines([ + ('INF.ERR', 'CRITICAL', '1'), + ('INF.ERR', 'ERROR', '2'), + ]) ->>> UNDEF.info (nextmessage()) -INFO:UNDEF0:Message 22 + def test_nested_inherited(self): + """ + Logging levels in a nested namespace, inherited from parent loggers. + """ + m = self.next_message ->>> GRANDCHILD.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.BADPARENT.UNDEF:Message 23 + INF = logging.getLogger("INF") + INF.setLevel(logging.INFO) + INF_ERR = logging.getLogger("INF.ERR") + INF_ERR.setLevel(logging.ERROR) + INF_UNDEF = logging.getLogger("INF.UNDEF") + INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF") + UNDEF = logging.getLogger("UNDEF") ->>> CHILD.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.BADPARENT:Message 24 + # These should log + INF_UNDEF.log(logging.FATAL, m()) + INF_UNDEF.error(m()) + INF_UNDEF.warn (m()) + INF_UNDEF.info (m()) + INF_ERR_UNDEF.log(logging.FATAL, m()) + INF_ERR_UNDEF.error(m()) -These should not log: + # These should not log + INF_UNDEF.debug(m()) + INF_ERR_UNDEF.warn(m()) + INF_ERR_UNDEF.info(m()) + INF_ERR_UNDEF.debug(m()) ->>> ERR.warn(nextmessage()) + self.assertLogLines([ + ('INF.UNDEF', 'CRITICAL', '1'), + ('INF.UNDEF', 'ERROR', '2'), + ('INF.UNDEF', 'WARNING', '3'), + ('INF.UNDEF', 'INFO', '4'), + ('INF.ERR.UNDEF', 'CRITICAL', '5'), + ('INF.ERR.UNDEF', 'ERROR', '6'), + ]) ->>> ERR.info(nextmessage()) + def test_nested_with_virtual_parent(self): + """ + Logging levels when some parent does not exist yet. + """ + m = self.next_message ->>> ERR.debug(nextmessage()) + INF = logging.getLogger("INF") + GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF") + CHILD = logging.getLogger("INF.BADPARENT") + INF.setLevel(logging.INFO) ->>> INF.debug(nextmessage()) + # These should log + GRANDCHILD.log(logging.FATAL, m()) + GRANDCHILD.info(m()) + CHILD.log(logging.FATAL, m()) + CHILD.info(m()) ->>> INF_UNDEF.debug(nextmessage()) + # These should not log + GRANDCHILD.debug(m()) + CHILD.debug(m()) ->>> INF_ERR.warn(nextmessage()) + self.assertLogLines([ + ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'), + ('INF.BADPARENT.UNDEF', 'INFO', '2'), + ('INF.BADPARENT', 'CRITICAL', '3'), + ('INF.BADPARENT', 'INFO', '4'), + ]) ->>> INF_ERR.info(nextmessage()) +tests.append(BuiltinLevelsTest) ->>> INF_ERR.debug(nextmessage()) ->>> INF_ERR_UNDEF.warn(nextmessage()) +class BasicFilterTest(BaseTest): + """ + Test the bundled Filter class. + """ ->>> INF_ERR_UNDEF.info(nextmessage()) + def test_filter(self): + """ + Only messages satisfying the specified criteria pass through the + filter. + """ + filt = logging.Filter("spam.eggs") + hand = self.root_logger.handlers[0] + try: + hand.addFilter(filt) + spam = logging.getLogger("spam") + spam_eggs = logging.getLogger("spam.eggs") + spam_eggs_fish = logging.getLogger("spam.eggs.fish") + spam_bakedbeans = logging.getLogger("spam.bakedbeans") + spam.info(self.next_message()) + spam_eggs.info(self.next_message()) + spam_eggs_fish.info(self.next_message()) + spam_bakedbeans.info(self.next_message()) + self.assertLogLines([ + ('spam.eggs', 'INFO', '2'), + ('spam.eggs.fish', 'INFO', '3'), + ]) + finally: + hand.removeFilter(filt) ->>> INF_ERR_UNDEF.debug(nextmessage()) +tests.append(BasicFilterTest) ->>> INF.info(FINISH_UP) -INFO:INFO0:Finish up, it's closing time. Messages should bear numbers 0 through 24. -Test 1 -====== ->>> import sys, logging ->>> logging.basicConfig(stream=sys.stdout) +# +# First, we define our levels. There can be as many as you want - the only +# limitations are that they should be integers, the lowest should be > 0 and +# larger values mean less information being logged. If you need specific +# level values which do not fit into these limitations, you can use a +# mapping dictionary to convert between your application levels and the +# logging system. +# +SILENT = 120 +TACITURN = 119 +TERSE = 118 +EFFUSIVE = 117 +SOCIABLE = 116 +VERBOSE = 115 +TALKATIVE = 114 +GARRULOUS = 113 +CHATTERBOX = 112 +BORING = 111 -First, we define our levels. There can be as many as you want - the only -limitations are that they should be integers, the lowest should be > 0 and -larger values mean less information being logged. If you need specific -level values which do not fit into these limitations, you can use a -mapping dictionary to convert between your application levels and the -logging system. +LEVEL_RANGE = range(BORING, SILENT + 1) ->>> SILENT = 10 ->>> TACITURN = 9 ->>> TERSE = 8 ->>> EFFUSIVE = 7 ->>> SOCIABLE = 6 ->>> VERBOSE = 5 ->>> TALKATIVE = 4 ->>> GARRULOUS = 3 ->>> CHATTERBOX = 2 ->>> BORING = 1 ->>> LEVEL_RANGE = range(BORING, SILENT + 1) +# +# Next, we define names for our levels. You don't need to do this - in which +# case the system will use "Level n" to denote the text for the level. +# +my_logging_levels = { + SILENT : 'Silent', + TACITURN : 'Taciturn', + TERSE : 'Terse', + EFFUSIVE : 'Effusive', + SOCIABLE : 'Sociable', + VERBOSE : 'Verbose', + TALKATIVE : 'Talkative', + GARRULOUS : 'Garrulous', + CHATTERBOX : 'Chatterbox', + BORING : 'Boring', +} +class GarrulousFilter(logging.Filter): + """ + A filter which blocks garrulous messages. + """ + def filter(self, record): + return record.levelno != GARRULOUS -Next, we define names for our levels. You don't need to do this - in which - case the system will use "Level n" to denote the text for the level. -' +class VerySpecificFilter(logging.Filter): + """ + A filter which blocks sociable and taciturn messages. + """ + def filter(self, record): + return record.levelno not in [SOCIABLE, TACITURN] ->>> my_logging_levels = { -... SILENT : 'SILENT', -... TACITURN : 'TACITURN', -... TERSE : 'TERSE', -... EFFUSIVE : 'EFFUSIVE', -... SOCIABLE : 'SOCIABLE', -... VERBOSE : 'VERBOSE', -... TALKATIVE : 'TALKATIVE', -... GARRULOUS : 'GARRULOUS', -... CHATTERBOX : 'CHATTERBOX', -... BORING : 'BORING', -... } +class CustomLevelsAndFiltersTest(BaseTest): + """ + Test various filtering possibilities with custom logging levels. + """ + expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$" + def setUp(self): + BaseTest.setUp(self) + for k, v in my_logging_levels.items(): + logging.addLevelName(k, v) -Now, to demonstrate filtering: suppose for some perverse reason we only -want to print out all except GARRULOUS messages. We create a filter for -this purpose... + def log_at_all_levels(self, logger): + for lvl in LEVEL_RANGE: + logger.log(lvl, self.next_message()) ->>> class SpecificLevelFilter(logging.Filter): -... def __init__(self, lvl): -... self.level = lvl -... -... def filter(self, record): -... return self.level != record.levelno + def test_logger_filter(self): + """ + Filter at logger level. + """ + self.root_logger.setLevel(VERBOSE) + self.log_at_all_levels(self.root_logger) + self.assertLogLines([ + ('Verbose', '5'), + ('Sociable', '6'), + ('Effusive', '7'), + ('Terse', '8'), + ('Taciturn', '9'), + ('Silent', '10'), + ]) ->>> class GarrulousFilter(SpecificLevelFilter): -... def __init__(self): -... SpecificLevelFilter.__init__(self, GARRULOUS) + def test_handler_filter(self): + """ + Filter at handler level. + """ + self.root_logger.handlers[0].setLevel(SOCIABLE) + try: + self.log_at_all_levels(self.root_logger) + self.assertLogLines([ + ('Sociable', '6'), + ('Effusive', '7'), + ('Terse', '8'), + ('Taciturn', '9'), + ('Silent', '10'), + ]) + finally: + self.root_logger.handlers[0].setLevel(0) + def test_specific_filters(self): + """ + Set a specific filter object on the handler, and then add another + filter object on the logger itself. + """ + hdlr = self.root_logger.handlers[0] + spec = None + garr = GarrulousFilter() + hdlr.addFilter(garr) + try: + self.log_at_all_levels(self.root_logger) + first_lines = [ + # Notice how 'Garrulous' is missing + ('Boring', '1'), + ('Chatterbox', '2'), + ('Talkative', '4'), + ('Verbose', '5'), + ('Sociable', '6'), + ('Effusive', '7'), + ('Terse', '8'), + ('Taciturn', '9'), + ('Silent', '10'), + ] + self.assertLogLines(first_lines) -Now, demonstrate filtering at the logger. This time, use a filter -which excludes SOCIABLE and TACITURN messages. Note that GARRULOUS events -are still excluded. + spec = VerySpecificFilter() + self.root_logger.addFilter(spec) + self.log_at_all_levels(self.root_logger) + self.assertLogLines(first_lines + [ + # Not only 'Garrulous' is still missing, but also 'Sociable' + # and 'Taciturn' + ('Boring', '11'), + ('Chatterbox', '12'), + ('Talkative', '14'), + ('Verbose', '15'), + ('Effusive', '17'), + ('Terse', '18'), + ('Silent', '20'), + ]) + finally: + if spec: + self.root_logger.removeFilter(spec) + hdlr.removeFilter(garr) +tests.append(CustomLevelsAndFiltersTest) ->>> class VerySpecificFilter(logging.Filter): -... def filter(self, record): -... return record.levelno not in [SOCIABLE, TACITURN] ->>> SHOULD1 = "This should only be seen at the '%s' logging level (or lower)" +class MemoryHandlerTest(BaseTest): + """ + Tests for the MemoryHandler. + """ + expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$" -Configure the logger, and tell the logging system to associate names with our levels. ->>> logging.basicConfig(stream=sys.stdout) ->>> rootLogger = logging.getLogger("") ->>> rootLogger.setLevel(logging.DEBUG) ->>> for lvl in my_logging_levels.keys(): -... logging.addLevelName(lvl, my_logging_levels[lvl]) ->>> log = logging.getLogger("") ->>> hdlr = log.handlers[0] ->>> from test_logging import message + def setUp(self): + BaseTest.setUp(self) + self.mem_hdlr = logging.handlers.MemoryHandler( + 10, logging.WARNING, self.root_hdlr) + self.mem_logger = logging.getLogger('mem') + self.mem_logger.propagate = 0 + self.mem_logger.addHandler(self.mem_hdlr) -Set the logging level to each different value and call the utility -function to log events. In the output, you should see that each time -round the loop, the number of logging events which are actually output -decreases. + def tearDown(self): + self.mem_hdlr.close() ->>> log.setLevel(1) + def test_flush(self): + """ + The memory handler flushes to its target handler based on specific + criteria (message count and message level). + """ + self.mem_logger.debug(self.next_message()) + self.assertLogLines([]) + self.mem_logger.info(self.next_message()) + self.assertLogLines([]) + # This will flush because the level is >= logging.WARNING + self.mem_logger.warn(self.next_message()) + lines = [ + ('DEBUG', '1'), + ('INFO', '2'), + ('WARNING', '3'), + ] + self.assertLogLines(lines) + for n in (4, 14): + for i in range(9): + self.mem_logger.debug(self.next_message()) + self.assertLogLines(lines) + # This will flush because it's the 10th message since the last flush. + self.mem_logger.debug(self.next_message()) + lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)] + self.assertLogLines(lines) ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) -BORING:root:This should only be seen at the '1' logging level (or lower) + self.mem_logger.debug(self.next_message()) + self.assertLogLines(lines) ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) -CHATTERBOX:root:This should only be seen at the '2' logging level (or lower) +tests.append(MemoryHandlerTest) ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) -GARRULOUS:root:This should only be seen at the '3' logging level (or lower) ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) +class ExceptionFormatter(logging.Formatter): + """ + A special exception formatter. + """ + def formatException(self, ei): + return "Got a [%s]" % ei[0].__name__ ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) +class ConfigFileTest(BaseTest): + """ + Reading logging config from a .ini-style config file. + """ ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) + expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$" ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) + # config0 is a standard configuration. + config0 = """ + [loggers] + keys=root ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) + [handlers] + keys=hand1 ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) + [formatters] + keys=form1 ->>> log.setLevel(0) + [logger_root] + level=WARNING + handlers=hand1 ->>> log.setLevel(2) + [handler_hand1] + class=StreamHandler + level=NOTSET + formatter=form1 + args=(sys.stdout,) ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) + [formatter_form1] + format=%(levelname)s ++ %(message)s + datefmt= + """ ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) -CHATTERBOX:root:This should only be seen at the '2' logging level (or lower) + # config1 adds a little to the standard configuration. + config1 = """ + [loggers] + keys=root,parser ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) -GARRULOUS:root:This should only be seen at the '3' logging level (or lower) + [handlers] + keys=hand1 ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) + [formatters] + keys=form1 ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) + [logger_root] + level=WARNING + handlers= ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) + [logger_parser] + level=DEBUG + handlers=hand1 + propagate=1 + qualname=compiler.parser ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) + [handler_hand1] + class=StreamHandler + level=NOTSET + formatter=form1 + args=(sys.stdout,) ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) + [formatter_form1] + format=%(levelname)s ++ %(message)s + datefmt= + """ ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) + # config2 has a subtle configuration error that should be reported + config2 = config1.replace("sys.stdout", "sys.stbout") ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) + # config3 has a less subtle configuration error + config3 = config1.replace("formatter=form1", "formatter=misspelled_name") ->>> log.setLevel(3) + # config4 specifies a custom formatter class to be loaded + config4 = """ + [loggers] + keys=root ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) + [handlers] + keys=hand1 ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) + [formatters] + keys=form1 ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) -GARRULOUS:root:This should only be seen at the '3' logging level (or lower) + [logger_root] + level=NOTSET + handlers=hand1 ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) + [handler_hand1] + class=StreamHandler + level=NOTSET + formatter=form1 + args=(sys.stdout,) ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) + [formatter_form1] + class=""" + __name__ + """.ExceptionFormatter + format=%(levelname)s:%(name)s:%(message)s + datefmt= + """ ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) + def apply_config(self, conf): + try: + fn = tempfile.mktemp(".ini") + f = open(fn, "w") + f.write(textwrap.dedent(conf)) + f.close() + logging.config.fileConfig(fn) + # "call again to make sure cleanup is correct" (???) + #logging.config.fileConfig(fn) + finally: + os.remove(fn) ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) + def test_config0_ok(self): + """ + A simple config file which overrides the default settings. + """ + old_stdout = sys.stdout + try: + sys.stdout = cStringIO.StringIO() + self.apply_config(self.config0) + logger = logging.getLogger() + # Won't output anything + logger.info(self.next_message()) + # Outputs a message + logger.error(self.next_message()) + self.assertLogLines([ + ('ERROR', '2'), + ], stream=sys.stdout) + # Original logger output is empty + self.assertLogLines([]) + finally: + sys.stdout = old_stdout ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) + def test_config1_ok(self): + """ + A config file defining a sub-parser as well. + """ + old_stdout = sys.stdout + try: + sys.stdout = cStringIO.StringIO() + self.apply_config(self.config1) + logger = logging.getLogger("compiler.parser") + # Both will output a message + logger.info(self.next_message()) + logger.error(self.next_message()) + self.assertLogLines([ + ('INFO', '1'), + ('ERROR', '2'), + ], stream=sys.stdout) + # Original logger output is empty + self.assertLogLines([]) + finally: + sys.stdout = old_stdout ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) + def test_config2_failure(self): + """ + A simple config file which overrides the default settings. + """ + self.assertRaises(StandardError, self.apply_config, self.config2) ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) + def test_config3_failure(self): + """ + A simple config file which overrides the default settings. + """ + self.assertRaises(StandardError, self.apply_config, self.config3) ->>> log.setLevel(4) + def test_config4_ok(self): + """ + A config file specifying a custom formatter class. + """ + old_stdout = sys.stdout + try: + sys.stdout = cStringIO.StringIO() + self.apply_config(self.config4) + logger = logging.getLogger() + try: + raise RuntimeError() + except RuntimeError: + logging.exception("just testing") + sys.stdout.seek(0) + self.assertEquals(sys.stdout.getvalue(), + "ERROR:root:just testing\nGot a [RuntimeError]\n") + # Original logger output is empty + self.assertLogLines([]) + finally: + sys.stdout = old_stdout ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) +tests.append(ConfigFileTest) ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(5) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - - ->>> log.setLevel(6) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(7) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(8) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(9) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(10) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> hdlr.setLevel(SOCIABLE) - ->>> log.setLevel(1) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(2) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(3) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(4) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(5) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(6) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(7) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(8) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(9) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(10) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> - ->>> hdlr.setLevel(0) - ->>> garr = GarrulousFilter() - ->>> hdlr.addFilter(garr) - ->>> log.setLevel(1) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) -BORING:root:This should only be seen at the '1' logging level (or lower) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) -CHATTERBOX:root:This should only be seen at the '2' logging level (or lower) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(2) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) -CHATTERBOX:root:This should only be seen at the '2' logging level (or lower) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(3) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(4) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(5) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.setLevel(6) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) -SOCIABLE:root:This should only be seen at the '6' logging level (or lower) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(7) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(8) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(9) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) -TACITURN:root:This should only be seen at the '9' logging level (or lower) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(10) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> spec = VerySpecificFilter() - ->>> log.addFilter(spec) - ->>> log.setLevel(1) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) -BORING:root:This should only be seen at the '1' logging level (or lower) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) -CHATTERBOX:root:This should only be seen at the '2' logging level (or lower) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(2) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) -CHATTERBOX:root:This should only be seen at the '2' logging level (or lower) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(3) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(4) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) -TALKATIVE:root:This should only be seen at the '4' logging level (or lower) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(5) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) -VERBOSE:root:This should only be seen at the '5' logging level (or lower) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(6) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(7) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) -EFFUSIVE:root:This should only be seen at the '7' logging level (or lower) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(8) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) -TERSE:root:This should only be seen at the '8' logging level (or lower) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(9) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(10) - ->>> log.log(1, "This should only be seen at the '%s' logging level (or lower)", BORING) - ->>> log.log(2, "This should only be seen at the '%s' logging level (or lower)", CHATTERBOX) - ->>> log.log(3, "This should only be seen at the '%s' logging level (or lower)", GARRULOUS) - ->>> log.log(4, "This should only be seen at the '%s' logging level (or lower)", TALKATIVE) - ->>> log.log(5, "This should only be seen at the '%s' logging level (or lower)", VERBOSE) - ->>> log.log(6, "This should only be seen at the '%s' logging level (or lower)", SOCIABLE) - ->>> log.log(7, "This should only be seen at the '%s' logging level (or lower)", EFFUSIVE) - ->>> log.log(8, "This should only be seen at the '%s' logging level (or lower)", TERSE) - ->>> log.log(9, "This should only be seen at the '%s' logging level (or lower)", TACITURN) - ->>> log.log(10, "This should only be seen at the '%s' logging level (or lower)", SILENT) -SILENT:root:This should only be seen at the '10' logging level (or lower) - ->>> log.setLevel(0) - ->>> log.removeFilter(spec) - ->>> hdlr.removeFilter(garr) - ->>> logging.addLevelName(logging.DEBUG, "DEBUG") - - -Test 2 -====== -Test memory handlers. These are basically buffers for log messages: they take so many messages, and then print them all. - ->>> import logging.handlers - ->>> sys.stderr = sys.stdout ->>> logger = logging.getLogger("") ->>> sh = logger.handlers[0] ->>> sh.close() ->>> logger.removeHandler(sh) ->>> mh = logging.handlers.MemoryHandler(10,logging.WARNING, sh) ->>> logger.setLevel(logging.DEBUG) ->>> logger.addHandler(mh) - ->>> logger.debug("Debug message") - --- logging at INFO, nothing should be seen yet -- - ->>> logger.info("Info message") - --- logging at WARNING, 3 messages should be seen -- - ->>> logger.warn("Warn message") -DEBUG:root:Debug message -INFO:root:Info message -WARNING:root:Warn message - ->>> logger.info("Info index = 0") - ->>> logger.info("Info index = 1") - ->>> logger.info("Info index = 2") - ->>> logger.info("Info index = 3") - ->>> logger.info("Info index = 4") - ->>> logger.info("Info index = 5") - ->>> logger.info("Info index = 6") - ->>> logger.info("Info index = 7") - ->>> logger.info("Info index = 8") - ->>> logger.info("Info index = 9") -INFO:root:Info index = 0 -INFO:root:Info index = 1 -INFO:root:Info index = 2 -INFO:root:Info index = 3 -INFO:root:Info index = 4 -INFO:root:Info index = 5 -INFO:root:Info index = 6 -INFO:root:Info index = 7 -INFO:root:Info index = 8 -INFO:root:Info index = 9 - ->>> logger.info("Info index = 10") - ->>> logger.info("Info index = 11") - ->>> logger.info("Info index = 12") - ->>> logger.info("Info index = 13") - ->>> logger.info("Info index = 14") - ->>> logger.info("Info index = 15") - ->>> logger.info("Info index = 16") - ->>> logger.info("Info index = 17") - ->>> logger.info("Info index = 18") - ->>> logger.info("Info index = 19") -INFO:root:Info index = 10 -INFO:root:Info index = 11 -INFO:root:Info index = 12 -INFO:root:Info index = 13 -INFO:root:Info index = 14 -INFO:root:Info index = 15 -INFO:root:Info index = 16 -INFO:root:Info index = 17 -INFO:root:Info index = 18 -INFO:root:Info index = 19 - ->>> logger.info("Info index = 20") - ->>> logger.info("Info index = 21") - ->>> logger.info("Info index = 22") - ->>> logger.info("Info index = 23") - ->>> logger.info("Info index = 24") - ->>> logger.info("Info index = 25") - ->>> logger.info("Info index = 26") - ->>> logger.info("Info index = 27") - ->>> logger.info("Info index = 28") - ->>> logger.info("Info index = 29") -INFO:root:Info index = 20 -INFO:root:Info index = 21 -INFO:root:Info index = 22 -INFO:root:Info index = 23 -INFO:root:Info index = 24 -INFO:root:Info index = 25 -INFO:root:Info index = 26 -INFO:root:Info index = 27 -INFO:root:Info index = 28 -INFO:root:Info index = 29 - ->>> logger.info("Info index = 30") - ->>> logger.info("Info index = 31") - ->>> logger.info("Info index = 32") - ->>> logger.info("Info index = 33") - ->>> logger.info("Info index = 34") - ->>> logger.info("Info index = 35") - ->>> logger.info("Info index = 36") - ->>> logger.info("Info index = 37") - ->>> logger.info("Info index = 38") - ->>> logger.info("Info index = 39") -INFO:root:Info index = 30 -INFO:root:Info index = 31 -INFO:root:Info index = 32 -INFO:root:Info index = 33 -INFO:root:Info index = 34 -INFO:root:Info index = 35 -INFO:root:Info index = 36 -INFO:root:Info index = 37 -INFO:root:Info index = 38 -INFO:root:Info index = 39 - ->>> logger.info("Info index = 40") - ->>> logger.info("Info index = 41") - ->>> logger.info("Info index = 42") - ->>> logger.info("Info index = 43") - ->>> logger.info("Info index = 44") - ->>> logger.info("Info index = 45") - ->>> logger.info("Info index = 46") - ->>> logger.info("Info index = 47") - ->>> logger.info("Info index = 48") - ->>> logger.info("Info index = 49") -INFO:root:Info index = 40 -INFO:root:Info index = 41 -INFO:root:Info index = 42 -INFO:root:Info index = 43 -INFO:root:Info index = 44 -INFO:root:Info index = 45 -INFO:root:Info index = 46 -INFO:root:Info index = 47 -INFO:root:Info index = 48 -INFO:root:Info index = 49 - ->>> logger.info("Info index = 50") - ->>> logger.info("Info index = 51") - ->>> logger.info("Info index = 52") - ->>> logger.info("Info index = 53") - ->>> logger.info("Info index = 54") - ->>> logger.info("Info index = 55") - ->>> logger.info("Info index = 56") - ->>> logger.info("Info index = 57") - ->>> logger.info("Info index = 58") - ->>> logger.info("Info index = 59") -INFO:root:Info index = 50 -INFO:root:Info index = 51 -INFO:root:Info index = 52 -INFO:root:Info index = 53 -INFO:root:Info index = 54 -INFO:root:Info index = 55 -INFO:root:Info index = 56 -INFO:root:Info index = 57 -INFO:root:Info index = 58 -INFO:root:Info index = 59 - ->>> logger.info("Info index = 60") - ->>> logger.info("Info index = 61") - ->>> logger.info("Info index = 62") - ->>> logger.info("Info index = 63") - ->>> logger.info("Info index = 64") - ->>> logger.info("Info index = 65") - ->>> logger.info("Info index = 66") - ->>> logger.info("Info index = 67") - ->>> logger.info("Info index = 68") - ->>> logger.info("Info index = 69") -INFO:root:Info index = 60 -INFO:root:Info index = 61 -INFO:root:Info index = 62 -INFO:root:Info index = 63 -INFO:root:Info index = 64 -INFO:root:Info index = 65 -INFO:root:Info index = 66 -INFO:root:Info index = 67 -INFO:root:Info index = 68 -INFO:root:Info index = 69 - ->>> logger.info("Info index = 70") - ->>> logger.info("Info index = 71") - ->>> logger.info("Info index = 72") - ->>> logger.info("Info index = 73") - ->>> logger.info("Info index = 74") - ->>> logger.info("Info index = 75") - ->>> logger.info("Info index = 76") - ->>> logger.info("Info index = 77") - ->>> logger.info("Info index = 78") - ->>> logger.info("Info index = 79") -INFO:root:Info index = 70 -INFO:root:Info index = 71 -INFO:root:Info index = 72 -INFO:root:Info index = 73 -INFO:root:Info index = 74 -INFO:root:Info index = 75 -INFO:root:Info index = 76 -INFO:root:Info index = 77 -INFO:root:Info index = 78 -INFO:root:Info index = 79 - ->>> logger.info("Info index = 80") - ->>> logger.info("Info index = 81") - ->>> logger.info("Info index = 82") - ->>> logger.info("Info index = 83") - ->>> logger.info("Info index = 84") - ->>> logger.info("Info index = 85") - ->>> logger.info("Info index = 86") - ->>> logger.info("Info index = 87") - ->>> logger.info("Info index = 88") - ->>> logger.info("Info index = 89") -INFO:root:Info index = 80 -INFO:root:Info index = 81 -INFO:root:Info index = 82 -INFO:root:Info index = 83 -INFO:root:Info index = 84 -INFO:root:Info index = 85 -INFO:root:Info index = 86 -INFO:root:Info index = 87 -INFO:root:Info index = 88 -INFO:root:Info index = 89 - ->>> logger.info("Info index = 90") - ->>> logger.info("Info index = 91") - ->>> logger.info("Info index = 92") - ->>> logger.info("Info index = 93") - ->>> logger.info("Info index = 94") - ->>> logger.info("Info index = 95") - ->>> logger.info("Info index = 96") - ->>> logger.info("Info index = 97") - ->>> logger.info("Info index = 98") - ->>> logger.info("Info index = 99") -INFO:root:Info index = 90 -INFO:root:Info index = 91 -INFO:root:Info index = 92 -INFO:root:Info index = 93 -INFO:root:Info index = 94 -INFO:root:Info index = 95 -INFO:root:Info index = 96 -INFO:root:Info index = 97 -INFO:root:Info index = 98 -INFO:root:Info index = 99 - ->>> logger.info("Info index = 100") - ->>> logger.info("Info index = 101") - ->>> mh.close() -INFO:root:Info index = 100 -INFO:root:Info index = 101 - ->>> logger.removeHandler(mh) ->>> logger.addHandler(sh) - - - -Test 3 -====== - ->>> import sys, logging ->>> sys.stderr = sys ->>> logging.basicConfig() ->>> FILTER = "a.b" ->>> root = logging.getLogger() ->>> root.setLevel(logging.DEBUG) ->>> hand = root.handlers[0] - ->>> logging.getLogger("a").info("Info 1") -INFO:a:Info 1 - ->>> logging.getLogger("a.b").info("Info 2") -INFO:a.b:Info 2 - ->>> logging.getLogger("a.c").info("Info 3") -INFO:a.c:Info 3 - ->>> logging.getLogger("a.b.c").info("Info 4") -INFO:a.b.c:Info 4 - ->>> logging.getLogger("a.b.c.d").info("Info 5") -INFO:a.b.c.d:Info 5 - ->>> logging.getLogger("a.bb.c").info("Info 6") -INFO:a.bb.c:Info 6 - ->>> logging.getLogger("b").info("Info 7") -INFO:b:Info 7 - ->>> logging.getLogger("b.a").info("Info 8") -INFO:b.a:Info 8 - ->>> logging.getLogger("c.a.b").info("Info 9") -INFO:c.a.b:Info 9 - ->>> logging.getLogger("a.bb").info("Info 10") -INFO:a.bb:Info 10 - -Filtered with 'a.b'... - ->>> filt = logging.Filter(FILTER) - ->>> hand.addFilter(filt) - ->>> logging.getLogger("a").info("Info 1") - ->>> logging.getLogger("a.b").info("Info 2") -INFO:a.b:Info 2 - ->>> logging.getLogger("a.c").info("Info 3") - ->>> logging.getLogger("a.b.c").info("Info 4") -INFO:a.b.c:Info 4 - ->>> logging.getLogger("a.b.c.d").info("Info 5") -INFO:a.b.c.d:Info 5 - ->>> logging.getLogger("a.bb.c").info("Info 6") - ->>> logging.getLogger("b").info("Info 7") - ->>> logging.getLogger("b.a").info("Info 8") - ->>> logging.getLogger("c.a.b").info("Info 9") - ->>> logging.getLogger("a.bb").info("Info 10") - ->>> hand.removeFilter(filt) - - -Test 4 -====== ->>> import sys, logging, logging.handlers, string ->>> import tempfile, logging.config, os, test.test_support ->>> sys.stderr = sys.stdout - ->>> from test_logging import config0, config1 - -config2 has a subtle configuration error that should be reported ->>> config2 = string.replace(config1, "sys.stdout", "sys.stbout") - -config3 has a less subtle configuration error ->>> config3 = string.replace(config1, "formatter=form1", "formatter=misspelled_name") - ->>> def test4(conf): -... loggerDict = logging.getLogger().manager.loggerDict -... logging._acquireLock() -... try: -... saved_handlers = logging._handlers.copy() -... saved_handler_list = logging._handlerList[:] -... saved_loggers = loggerDict.copy() -... finally: -... logging._releaseLock() -... try: -... fn = test.test_support.TESTFN -... f = open(fn, "w") -... f.write(conf) -... f.close() -... try: -... logging.config.fileConfig(fn) -... #call again to make sure cleanup is correct -... logging.config.fileConfig(fn) -... except: -... t = sys.exc_info()[0] -... message(str(t)) -... else: -... message('ok.') -... os.remove(fn) -... finally: -... logging._acquireLock() -... try: -... logging._handlers.clear() -... logging._handlers.update(saved_handlers) -... logging._handlerList[:] = saved_handler_list -... loggerDict = logging.getLogger().manager.loggerDict -... loggerDict.clear() -... loggerDict.update(saved_loggers) -... finally: -... logging._releaseLock() - ->>> test4(config0) -ok. - ->>> test4(config1) -ok. - ->>> test4(config2) - - ->>> test4(config3) - - ->>> import test_logging ->>> test_logging.test5() -ERROR:root:just testing -... Don't panic! - - -Test Main -========= ->>> import select ->>> import os, sys, string, struct, types, cPickle, cStringIO ->>> import socket, tempfile, threading, time ->>> import logging, logging.handlers, logging.config ->>> import test_logging - ->>> test_logging.test_main_inner() -ERR -> CRITICAL: Message 0 (via logrecv.tcp.ERR) -ERR -> ERROR: Message 1 (via logrecv.tcp.ERR) -INF -> CRITICAL: Message 2 (via logrecv.tcp.INF) -INF -> ERROR: Message 3 (via logrecv.tcp.INF) -INF -> WARNING: Message 4 (via logrecv.tcp.INF) -INF -> INFO: Message 5 (via logrecv.tcp.INF) -INF.UNDEF -> CRITICAL: Message 6 (via logrecv.tcp.INF.UNDEF) -INF.UNDEF -> ERROR: Message 7 (via logrecv.tcp.INF.UNDEF) -INF.UNDEF -> WARNING: Message 8 (via logrecv.tcp.INF.UNDEF) -INF.UNDEF -> INFO: Message 9 (via logrecv.tcp.INF.UNDEF) -INF.ERR -> CRITICAL: Message 10 (via logrecv.tcp.INF.ERR) -INF.ERR -> ERROR: Message 11 (via logrecv.tcp.INF.ERR) -INF.ERR.UNDEF -> CRITICAL: Message 12 (via logrecv.tcp.INF.ERR.UNDEF) -INF.ERR.UNDEF -> ERROR: Message 13 (via logrecv.tcp.INF.ERR.UNDEF) -DEB -> CRITICAL: Message 14 (via logrecv.tcp.DEB) -DEB -> ERROR: Message 15 (via logrecv.tcp.DEB) -DEB -> WARNING: Message 16 (via logrecv.tcp.DEB) -DEB -> INFO: Message 17 (via logrecv.tcp.DEB) -DEB -> DEBUG: Message 18 (via logrecv.tcp.DEB) -UNDEF -> CRITICAL: Message 19 (via logrecv.tcp.UNDEF) -UNDEF -> ERROR: Message 20 (via logrecv.tcp.UNDEF) -UNDEF -> WARNING: Message 21 (via logrecv.tcp.UNDEF) -UNDEF -> INFO: Message 22 (via logrecv.tcp.UNDEF) -INF.BADPARENT.UNDEF -> CRITICAL: Message 23 (via logrecv.tcp.INF.BADPARENT.UNDEF) -INF.BADPARENT -> CRITICAL: Message 24 (via logrecv.tcp.INF.BADPARENT) -INF -> INFO: Finish up, it's closing time. Messages should bear numbers 0 through 24. (via logrecv.tcp.INF) - -""" -import select -import os, sys, string, struct, cPickle, cStringIO -import socket, threading -import logging, logging.handlers, logging.config, test.test_support - - -BANNER = "-- %-10s %-6s ---------------------------------------------------\n" - -FINISH_UP = "Finish up, it's closing time. Messages should bear numbers 0 through 24." -#---------------------------------------------------------------------------- -# Test 0 -#---------------------------------------------------------------------------- - -msgcount = 0 - -def nextmessage(): - global msgcount - rv = "Message %d" % msgcount - msgcount = msgcount + 1 - return rv - -#---------------------------------------------------------------------------- -# Log receiver -#---------------------------------------------------------------------------- - -TIMEOUT = 10 - -from SocketServer import ThreadingTCPServer, StreamRequestHandler +TCP_LOG_END = "!!!END!!!" class LogRecordStreamHandler(StreamRequestHandler): """ - Handler for a streaming logging request. It basically logs the record - using whatever logging policy is configured locally. + Handler for a streaming logging request. It saves the log message in the + TCP server's 'log_output' attribute. """ def handle(self): @@ -1898,103 +708,27 @@ class LogRecordStreamHandler(StreamReque followed by the LogRecord in pickle format. Logs the record according to whatever policy is configured locally. """ - while 1: - try: - chunk = self.connection.recv(4) - if len(chunk) < 4: - break - slen = struct.unpack(">L", chunk)[0] - chunk = self.connection.recv(slen) - while len(chunk) < slen: - chunk = chunk + self.connection.recv(slen - len(chunk)) - obj = self.unPickle(chunk) - record = logging.makeLogRecord(obj) - self.handleLogRecord(record) - except: - raise + while True: + chunk = self.connection.recv(4) + if len(chunk) < 4: + break + slen = struct.unpack(">L", chunk)[0] + chunk = self.connection.recv(slen) + while len(chunk) < slen: + chunk = chunk + self.connection.recv(slen - len(chunk)) + obj = self.unPickle(chunk) + record = logging.makeLogRecord(obj) + self.handleLogRecord(record) def unPickle(self, data): return cPickle.loads(data) def handleLogRecord(self, record): - logname = "logrecv.tcp." + record.name - #If the end-of-messages sentinel is seen, tell the server to terminate - if record.msg == FINISH_UP: + # If the end-of-messages sentinel is seen, tell the server to terminate + if TCP_LOG_END in record.msg: self.server.abort = 1 - record.msg = record.msg + " (via " + logname + ")" - logger = logging.getLogger("logrecv") - logger.handle(record) - -# The server sets socketDataProcessed when it's done. -socketDataProcessed = threading.Event() -#---------------------------------------------------------------------------- -# Test 5 -#---------------------------------------------------------------------------- - -test5_config = """ -[loggers] -keys=root - -[handlers] -keys=hand1 - -[formatters] -keys=form1 - -[logger_root] -level=NOTSET -handlers=hand1 - -[handler_hand1] -class=StreamHandler -level=NOTSET -formatter=form1 -args=(sys.stdout,) - -[formatter_form1] -class=test.test_logging.FriendlyFormatter -format=%(levelname)s:%(name)s:%(message)s -datefmt= -""" - -class FriendlyFormatter (logging.Formatter): - def formatException(self, ei): - return "%s... Don't panic!" % str(ei[0]) - - -def test5(): - loggerDict = logging.getLogger().manager.loggerDict - logging._acquireLock() - try: - saved_handlers = logging._handlers.copy() - saved_handler_list = logging._handlerList[:] - saved_loggers = loggerDict.copy() - finally: - logging._releaseLock() - try: - fn = test.test_support.TESTFN - f = open(fn, "w") - f.write(test5_config) - f.close() - logging.config.fileConfig(fn) - try: - raise KeyError - except KeyError: - logging.exception("just testing") - os.remove(fn) - hdlr = logging.getLogger().handlers[0] - logging.getLogger().handlers.remove(hdlr) - finally: - logging._acquireLock() - try: - logging._handlers.clear() - logging._handlers.update(saved_handlers) - logging._handlerList[:] = saved_handler_list - loggerDict = logging.getLogger().manager.loggerDict - loggerDict.clear() - loggerDict.update(saved_loggers) - finally: - logging._releaseLock() + return + self.server.log_output += record.msg + "\n" class LogRecordSocketReceiver(ThreadingTCPServer): @@ -2002,15 +736,16 @@ class LogRecordSocketReceiver(ThreadingT A simple-minded TCP socket-based logging receiver suitable for test purposes. """ - allow_reuse_address = 1 + log_output = "" def __init__(self, host='localhost', port=logging.handlers.DEFAULT_TCP_LOGGING_PORT, handler=LogRecordStreamHandler): ThreadingTCPServer.__init__(self, (host, port), handler) self.abort = False - self.timeout = 1 + self.timeout = 0.1 + self.finished = threading.Event() def serve_until_stopped(self): while not self.abort: @@ -2018,217 +753,148 @@ class LogRecordSocketReceiver(ThreadingT self.timeout) if rd: self.handle_request() - socketDataProcessed.set() + # Notify the main thread that we're about to exit + self.finished.set() # close the listen socket self.server_close() - def process_request(self, request, client_address): - t = threading.Thread(target = self.finish_request, - args = (request, client_address)) - t.start() -def runTCP(tcpserver): - tcpserver.serve_until_stopped() +class SocketHandlerTest(BaseTest): + """ + Test for SocketHandler objects. + """ -def banner(nm, typ): - sep = BANNER % (nm, typ) - sys.stdout.write(sep) - sys.stdout.flush() + def setUp(self): + """ + Set up a TCP server to receive log messages, and a SocketHandler + pointing to that server's address and port. + """ + BaseTest.setUp(self) -def test0(): - ERR = logging.getLogger("ERR") - ERR.setLevel(logging.ERROR) - INF = logging.getLogger("INF") - INF.setLevel(logging.INFO) - INF_ERR = logging.getLogger("INF.ERR") - INF_ERR.setLevel(logging.ERROR) - DEB = logging.getLogger("DEB") - DEB.setLevel(logging.DEBUG) + # Find an unused port number + self.port = logging.handlers.DEFAULT_TCP_LOGGING_PORT + while self.port < logging.handlers.DEFAULT_TCP_LOGGING_PORT+100: + try: + self.tcpserver = LogRecordSocketReceiver(port=self.port) + except socket.error: + self.port += 1 + else: + break + else: + raise RuntimeError("Could not find unused port") - INF_UNDEF = logging.getLogger("INF.UNDEF") - INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF") - UNDEF = logging.getLogger("UNDEF") + self.threads = [threading.Thread(target=self.tcpserver.serve_until_stopped)] + for thread in self.threads: + thread.start() - GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF") - CHILD = logging.getLogger("INF.BADPARENT") + self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port) + self.sock_hdlr.setFormatter(self.root_formatter) + self.root_logger.removeHandler(self.root_logger.handlers[0]) + self.root_logger.addHandler(self.sock_hdlr) - #These should log - ERR.log(logging.FATAL, nextmessage()) - ERR.error(nextmessage()) + def tearDown(self): + """ + Shutdown the TCP server. + """ + try: + self.tcpserver.abort = True + del self.tcpserver + self.root_logger.removeHandler(self.sock_hdlr) + self.sock_hdlr.close() + for thread in self.threads: + thread.join(2.0) + finally: + BaseTest.tearDown(self) - INF.log(logging.FATAL, nextmessage()) - INF.error(nextmessage()) - INF.warn(nextmessage()) - INF.info(nextmessage()) + def get_output(self): + """ + Get the log output as received by the TCP server. + """ + # Signal the TCP receiver and wait for it to terminate + self.root_logger.critical(TCP_LOG_END) + self.tcpserver.finished.wait(2.0) + return self.tcpserver.log_output - INF_UNDEF.log(logging.FATAL, nextmessage()) - INF_UNDEF.error(nextmessage()) - INF_UNDEF.warn (nextmessage()) - INF_UNDEF.info (nextmessage()) + def test_output(self): + """ + The log message sent to the SocketHandler is properly received. + """ + logger = logging.getLogger("tcp") + logger.error("spam") + logger.debug("eggs") + self.assertEquals(self.get_output(), "spam\neggs\n") - INF_ERR.log(logging.FATAL, nextmessage()) - INF_ERR.error(nextmessage()) +tests.append(SocketHandlerTest) - INF_ERR_UNDEF.log(logging.FATAL, nextmessage()) - INF_ERR_UNDEF.error(nextmessage()) - DEB.log(logging.FATAL, nextmessage()) - DEB.error(nextmessage()) - DEB.warn (nextmessage()) - DEB.info (nextmessage()) - DEB.debug(nextmessage()) +class MemoryTest(BaseTest): + """ + Test memory persistence of logger objects. + """ - UNDEF.log(logging.FATAL, nextmessage()) - UNDEF.error(nextmessage()) - UNDEF.warn (nextmessage()) - UNDEF.info (nextmessage()) + def setUp(self): + """ + Create a dict to remember potentially destroyed objects. + """ + BaseTest.setUp(self) + self._survivors = {} - GRANDCHILD.log(logging.FATAL, nextmessage()) - CHILD.log(logging.FATAL, nextmessage()) + def _watchForSurvival(self, *args): + """ + Watch the given objects for survival, by creating weakrefs to them. + """ + for obj in args: + key = id(obj), repr(obj) + self._survivors[key] = weakref.ref(obj) - #These should not log - ERR.warn(nextmessage()) - ERR.info(nextmessage()) - ERR.debug(nextmessage()) + def _assertSurvival(self): + """ + Assert that all objects watched for survival have survived. + """ + # Trigger cycle breaking + gc.collect() + dead = [] + for (id_, repr_), ref in self._survivors.items(): + if ref() is None: + dead.append(repr_) + if dead: + self.fail("%d objects should have survived " + "but have been destroyed: %s" % (len(dead), ", ".join(dead))) - INF.debug(nextmessage()) - INF_UNDEF.debug(nextmessage()) + def test_persistent_loggers(self): + """ + Logger objects are persistent and retain their configuration, even + if visible references are destroyed. + """ + self.root_logger.setLevel(logging.INFO) + foo = logging.getLogger("foo") + self._watchForSurvival(foo) + foo.setLevel(logging.DEBUG) + self.root_logger.debug(self.next_message()) + foo.debug(self.next_message()) + self.assertLogLines([ + ('foo', 'DEBUG', '2'), + ]) + del foo + # foo has survived + self._assertSurvival() + # foo has retained its settings + bar = logging.getLogger("foo") + bar.debug(self.next_message()) + self.assertLogLines([ + ('foo', 'DEBUG', '2'), + ('foo', 'DEBUG', '3'), + ]) - INF_ERR.warn(nextmessage()) - INF_ERR.info(nextmessage()) - INF_ERR.debug(nextmessage()) - INF_ERR_UNDEF.warn(nextmessage()) - INF_ERR_UNDEF.info(nextmessage()) - INF_ERR_UNDEF.debug(nextmessage()) +tests.append(MemoryTest) - INF.info(FINISH_UP) -def test_main_inner(): - rootLogger = logging.getLogger("") - rootLogger.setLevel(logging.DEBUG) +# Set the locale to the platform-dependent default. I have no idea +# why the test does this, but in any case we save the current locale +# first and restore it at the end. +@run_with_locale('LC_ALL', '') +def test_main(): + run_unittest(*tests) - tcpserver = LogRecordSocketReceiver(port=0) - port = tcpserver.socket.getsockname()[1] - - # Set up a handler such that all events are sent via a socket to the log - # receiver (logrecv). - # The handler will only be added to the rootLogger for some of the tests - shdlr = logging.handlers.SocketHandler('localhost', port) - rootLogger.addHandler(shdlr) - - # Configure the logger for logrecv so events do not propagate beyond it. - # The sockLogger output is buffered in memory until the end of the test, - # and printed at the end. - sockOut = cStringIO.StringIO() - sockLogger = logging.getLogger("logrecv") - sockLogger.setLevel(logging.DEBUG) - sockhdlr = logging.StreamHandler(sockOut) - sockhdlr.setFormatter(logging.Formatter( - "%(name)s -> %(levelname)s: %(message)s")) - sockLogger.addHandler(sockhdlr) - sockLogger.propagate = 0 - - #Set up servers - threads = [] - #sys.stdout.write("About to start TCP server...\n") - threads.append(threading.Thread(target=runTCP, args=(tcpserver,))) - - for thread in threads: - thread.start() - try: - test0() - - # XXX(nnorwitz): Try to fix timing related test failures. - # This sleep gives us some extra time to read messages. - # The test generally only fails on Solaris without this sleep. - #time.sleep(2.0) - shdlr.close() - rootLogger.removeHandler(shdlr) - - finally: - #wait for TCP receiver to terminate - socketDataProcessed.wait() - # ensure the server dies - tcpserver.abort = True - for thread in threads: - thread.join(2.0) - print(sockOut.getvalue()) - sockOut.close() - sockLogger.removeHandler(sockhdlr) - sockhdlr.close() - sys.stdout.flush() - -# config0 is a standard configuration. -config0 = """ -[loggers] -keys=root - -[handlers] -keys=hand1 - -[formatters] -keys=form1 - -[logger_root] -level=NOTSET -handlers=hand1 - -[handler_hand1] -class=StreamHandler -level=NOTSET -formatter=form1 -args=(sys.stdout,) - -[formatter_form1] -format=%(levelname)s:%(name)s:%(message)s -datefmt= -""" - -# config1 adds a little to the standard configuration. -config1 = """ -[loggers] -keys=root,parser - -[handlers] -keys=hand1 - -[formatters] -keys=form1 - -[logger_root] -level=NOTSET -handlers=hand1 - -[logger_parser] -level=DEBUG -handlers=hand1 -propagate=1 -qualname=compiler.parser - -[handler_hand1] -class=StreamHandler -level=NOTSET -formatter=form1 -args=(sys.stdout,) - -[formatter_form1] -format=%(levelname)s:%(name)s:%(message)s -datefmt= -""" - -def message(s): - sys.stdout.write("%s\n" % s) - -# config2 has a subtle configuration error that should be reported -config2 = string.replace(config1, "sys.stdout", "sys.stbout") - -# config3 has a less subtle configuration error -config3 = string.replace( - config1, "formatter=form1", "formatter=misspelled_name") - -def test_main(): - from test import test_support, test_logging - test_support.run_doctest(test_logging) - -if __name__=="__main__": +if __name__ == "__main__": test_main()