From 56c4debe0d005c48e53fdafbf64aef894a4c5fc6 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 3 Mar 2008 00:38:58 +0000 Subject: [PATCH] Refactor test_logging to use unittest. This should finally solve the flakiness issues. Thanks to Antoine Pitrou for the patch. --- Lib/test/test_logging.py | 2952 ++++++++++---------------------------- Misc/ACKS | 1 + Misc/NEWS | 2 +- 3 files changed, 772 insertions(+), 2183 deletions(-) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 5a96664c29d..1fac498d027 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1,2016 +1,702 @@ #!/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. + +"""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 logging +import logging.handlers +import logging.config -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) - -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") - - -And finally, run all the tests. - ->>> ERR.log(logging.FATAL, nextmessage()) -CRITICAL:ERR0:Message 0 - ->>> ERR.error(nextmessage()) -ERROR:ERR0:Message 1 - ->>> INF.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0:Message 2 - ->>> INF.error(nextmessage()) -ERROR:INFO0:Message 3 - ->>> INF.warn(nextmessage()) -WARNING:INFO0:Message 4 - ->>> INF.info(nextmessage()) -INFO:INFO0:Message 5 - ->>> INF_UNDEF.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.UNDEF:Message 6 - ->>> INF_UNDEF.error(nextmessage()) -ERROR:INFO0.UNDEF:Message 7 - ->>> INF_UNDEF.warn (nextmessage()) -WARNING:INFO0.UNDEF:Message 8 - ->>> INF_UNDEF.info (nextmessage()) -INFO:INFO0.UNDEF:Message 9 - ->>> INF_ERR.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.ERR:Message 10 - ->>> INF_ERR.error(nextmessage()) -ERROR:INFO0.ERR:Message 11 - ->>> INF_ERR_UNDEF.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.ERR.UNDEF:Message 12 - ->>> INF_ERR_UNDEF.error(nextmessage()) -ERROR:INFO0.ERR.UNDEF:Message 13 - ->>> DEB.log(logging.FATAL, nextmessage()) -CRITICAL:DEB0:Message 14 - ->>> DEB.error(nextmessage()) -ERROR:DEB0:Message 15 - ->>> DEB.warn (nextmessage()) -WARNING:DEB0:Message 16 - ->>> DEB.info (nextmessage()) -INFO:DEB0:Message 17 - ->>> DEB.debug(nextmessage()) -DEBUG:DEB0:Message 18 - ->>> UNDEF.log(logging.FATAL, nextmessage()) -CRITICAL:UNDEF0:Message 19 - ->>> UNDEF.error(nextmessage()) -ERROR:UNDEF0:Message 20 - ->>> UNDEF.warn (nextmessage()) -WARNING:UNDEF0:Message 21 - ->>> UNDEF.info (nextmessage()) -INFO:UNDEF0:Message 22 - ->>> GRANDCHILD.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.BADPARENT.UNDEF:Message 23 - ->>> CHILD.log(logging.FATAL, nextmessage()) -CRITICAL:INFO0.BADPARENT:Message 24 - -These should not log: - ->>> ERR.warn(nextmessage()) - ->>> ERR.info(nextmessage()) - ->>> ERR.debug(nextmessage()) - ->>> INF.debug(nextmessage()) - ->>> INF_UNDEF.debug(nextmessage()) - ->>> 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()) - ->>> 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 = 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', -... } - - -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... - ->>> class SpecificLevelFilter(logging.Filter): -... def __init__(self, lvl): -... self.level = lvl -... -... def filter(self, record): -... return self.level != record.levelno - ->>> class GarrulousFilter(SpecificLevelFilter): -... def __init__(self): -... SpecificLevelFilter.__init__(self, GARRULOUS) - - -Now, demonstrate filtering at the logger. This time, use a filter -which excludes SOCIABLE and TACITURN messages. Note that GARRULOUS events -are still excluded. - - ->>> 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)" - -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 - -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. - ->>> 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) -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) - ->>> 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(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) -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) - ->>> 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) -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) - ->>> 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(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 copy +import cPickle +import cStringIO +import gc +import os +import re 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 - +import socket from SocketServer import ThreadingTCPServer, StreamRequestHandler - -class LogRecordStreamHandler(StreamRequestHandler): - """ - Handler for a streaming logging request. It basically logs the record - using whatever logging policy is configured locally. - """ - - def handle(self): - """ - Handle multiple requests - each expected to be a 4-byte length, - 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 - - 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: - 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]) +import string +import struct +import sys +import tempfile +from test.test_support import captured_stdout, run_with_locale, run_unittest +import textwrap +import threading +import time +import types +import unittest +import weakref -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: +class BaseTest(unittest.TestCase): + + """Base class for logging tests.""" + + log_format = "%(name)s -> %(levelname)s: %(message)s" + expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$" + message_num = 0 + + def setUp(self): + """Setup the default logging stream to an internal StringIO instance, + so that we can examine log output as we want.""" + logger_dict = logging.getLogger().manager.loggerDict 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) + self.saved_handlers = logging._handlers.copy() + self.saved_handler_list = logging._handlerList[:] + self.saved_loggers = logger_dict.copy() + self.saved_level_names = logging._levelNames.copy() finally: logging._releaseLock() + self.root_logger = logging.getLogger("") + self.original_logging_level = self.root_logger.getEffectiveLevel() + + 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) + + 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() + + def assert_log_lines(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) + try: + stream.reset() + actual_lines = stream.readlines() + except AttributeError: + # StringIO.StringIO lacks a reset() method. + actual_lines = stream.getvalue().splitlines() + 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) + + def next_message(self): + """Generate a message consisting solely of an auto-incrementing + integer.""" + self.message_num += 1 + return "%d" % self.message_num + + +class BuiltinLevelsTest(BaseTest): + """Test builtin levels and their inheritance.""" + + def test_flat(self): + #Logging levels in a flat logger namespace. + m = self.next_message + + ERR = logging.getLogger("ERR") + ERR.setLevel(logging.ERROR) + INF = logging.getLogger("INF") + INF.setLevel(logging.INFO) + DEB = logging.getLogger("DEB") + DEB.setLevel(logging.DEBUG) + + # These should log. + ERR.log(logging.CRITICAL, m()) + ERR.error(m()) + + INF.log(logging.CRITICAL, m()) + INF.error(m()) + INF.warn(m()) + INF.info(m()) + + DEB.log(logging.CRITICAL, m()) + DEB.error(m()) + DEB.warn (m()) + DEB.info (m()) + DEB.debug(m()) + + # These should not log. + ERR.warn(m()) + ERR.info(m()) + ERR.debug(m()) + + INF.debug(m()) + + self.assert_log_lines([ + ('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'), + ]) + + def test_nested_explicit(self): + # Logging levels in a nested namespace, all explicitly set. + m = self.next_message + + INF = logging.getLogger("INF") + INF.setLevel(logging.INFO) + INF_ERR = logging.getLogger("INF.ERR") + INF_ERR.setLevel(logging.ERROR) + + # These should log. + INF_ERR.log(logging.CRITICAL, m()) + INF_ERR.error(m()) + + # These should not log. + INF_ERR.warn(m()) + INF_ERR.info(m()) + INF_ERR.debug(m()) + + self.assert_log_lines([ + ('INF.ERR', 'CRITICAL', '1'), + ('INF.ERR', 'ERROR', '2'), + ]) + + def test_nested_inherited(self): + #Logging levels in a nested namespace, inherited from parent loggers. + m = self.next_message + + 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") + + # These should log. + INF_UNDEF.log(logging.CRITICAL, m()) + INF_UNDEF.error(m()) + INF_UNDEF.warn(m()) + INF_UNDEF.info(m()) + INF_ERR_UNDEF.log(logging.CRITICAL, m()) + INF_ERR_UNDEF.error(m()) + + # These should not log. + INF_UNDEF.debug(m()) + INF_ERR_UNDEF.warn(m()) + INF_ERR_UNDEF.info(m()) + INF_ERR_UNDEF.debug(m()) + + self.assert_log_lines([ + ('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'), + ]) + + def test_nested_with_virtual_parent(self): + # Logging levels when some parent does not exist yet. + m = self.next_message + + INF = logging.getLogger("INF") + GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF") + CHILD = logging.getLogger("INF.BADPARENT") + INF.setLevel(logging.INFO) + + # These should log. + GRANDCHILD.log(logging.FATAL, m()) + GRANDCHILD.info(m()) + CHILD.log(logging.FATAL, m()) + CHILD.info(m()) + + # These should not log. + GRANDCHILD.debug(m()) + CHILD.debug(m()) + + self.assert_log_lines([ + ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'), + ('INF.BADPARENT.UNDEF', 'INFO', '2'), + ('INF.BADPARENT', 'CRITICAL', '3'), + ('INF.BADPARENT', 'INFO', '4'), + ]) + + +class BasicFilterTest(BaseTest): + + """Test the bundled Filter class.""" + + def test_filter(self): + # Only messages satisfying the specified criteria pass through the + # filter. + filter_ = logging.Filter("spam.eggs") + handler = self.root_logger.handlers[0] + try: + handler.addFilter(filter_) + 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()) # Good. + spam_eggs_fish.info(self.next_message()) # Good. + spam_bakedbeans.info(self.next_message()) + + self.assert_log_lines([ + ('spam.eggs', 'INFO', '2'), + ('spam.eggs.fish', 'INFO', '3'), + ]) + finally: + handler.removeFilter(filter_) + + +# +# 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 + +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 + +class VerySpecificFilter(logging.Filter): + + """A filter which blocks sociable and taciturn messages.""" + + def filter(self, record): + return record.levelno not in [SOCIABLE, TACITURN] + + +class CustomLevelsAndFiltersTest(BaseTest): + + """Test various filtering possibilities with custom logging levels.""" + + # Skip the logger name group. + 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) + + def log_at_all_levels(self, logger): + for lvl in LEVEL_RANGE: + logger.log(lvl, self.next_message()) + + def test_logger_filter(self): + # Filter at logger level. + self.root_logger.setLevel(VERBOSE) + # Levels >= 'Verbose' are good. + self.log_at_all_levels(self.root_logger) + self.assert_log_lines([ + ('Verbose', '5'), + ('Sociable', '6'), + ('Effusive', '7'), + ('Terse', '8'), + ('Taciturn', '9'), + ('Silent', '10'), + ]) + + def test_handler_filter(self): + # Filter at handler level. + self.root_logger.handlers[0].setLevel(SOCIABLE) + try: + # Levels >= 'Sociable' are good. + self.log_at_all_levels(self.root_logger) + self.assert_log_lines([ + ('Sociable', '6'), + ('Effusive', '7'), + ('Terse', '8'), + ('Taciturn', '9'), + ('Silent', '10'), + ]) + finally: + self.root_logger.handlers[0].setLevel(logging.NOTSET) + + def test_specific_filters(self): + # Set a specific filter object on the handler, and then add another + # filter object on the logger itself. + handler = self.root_logger.handlers[0] + specific_filter = None + garr = GarrulousFilter() + handler.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.assert_log_lines(first_lines) + + specific_filter = VerySpecificFilter() + self.root_logger.addFilter(specific_filter) + self.log_at_all_levels(self.root_logger) + self.assert_log_lines(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 specific_filter: + self.root_logger.removeFilter(specific_filter) + handler.removeFilter(garr) + + +class MemoryHandlerTest(BaseTest): + + """Tests for the MemoryHandler.""" + + # Do not bother with a logger name group. + expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$" + + 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) + + def tearDown(self): + self.mem_hdlr.close() + + 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.assert_log_lines([]) + self.mem_logger.info(self.next_message()) + self.assert_log_lines([]) + # This will flush because the level is >= logging.WARNING + self.mem_logger.warn(self.next_message()) + lines = [ + ('DEBUG', '1'), + ('INFO', '2'), + ('WARNING', '3'), + ] + self.assert_log_lines(lines) + for n in (4, 14): + for i in range(9): + self.mem_logger.debug(self.next_message()) + self.assert_log_lines(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.assert_log_lines(lines) + + self.mem_logger.debug(self.next_message()) + self.assert_log_lines(lines) + + +class ExceptionFormatter(logging.Formatter): + """A special exception formatter.""" + def formatException(self, ei): + return "Got a [%s]" % ei[0].__name__ + + +class ConfigFileTest(BaseTest): + + """Reading logging config from a .ini-style config file.""" + + expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$" + + # config0 is a standard configuration. + config0 = """ + [loggers] + keys=root + + [handlers] + keys=hand1 + + [formatters] + keys=form1 + + [logger_root] + level=WARNING + handlers=hand1 + + [handler_hand1] + class=StreamHandler + level=NOTSET + formatter=form1 + args=(sys.stdout,) + + [formatter_form1] + format=%(levelname)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=WARNING + handlers= + + [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 ++ %(message)s + datefmt= + """ + + # config2 has a subtle configuration error that should be reported + config2 = config1.replace("sys.stdout", "sys.stbout") + + # config3 has a less subtle configuration error + config3 = config1.replace("formatter=form1", "formatter=misspelled_name") + + # config4 specifies a custom formatter class to be loaded + config4 = """ + [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=""" + __name__ + """.ExceptionFormatter + format=%(levelname)s:%(name)s:%(message)s + datefmt= + """ + + 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) + finally: + os.remove(fn) + + def test_config0_ok(self): + # A simple config file which overrides the default settings. + with captured_stdout() as output: + 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.assert_log_lines([ + ('ERROR', '2'), + ], stream=output) + # Original logger output is empty. + self.assert_log_lines([]) + + def test_config1_ok(self): + # A config file defining a sub-parser as well. + with captured_stdout() as output: + 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.assert_log_lines([ + ('INFO', '1'), + ('ERROR', '2'), + ], stream=output) + # Original logger output is empty. + self.assert_log_lines([]) + + def test_config2_failure(self): + # A simple config file which overrides the default settings. + self.assertRaises(StandardError, self.apply_config, self.config2) + + def test_config3_failure(self): + # A simple config file which overrides the default settings. + self.assertRaises(StandardError, self.apply_config, self.config3) + + def test_config4_ok(self): + # A config file specifying a custom formatter class. + with captured_stdout() as output: + self.apply_config(self.config4) + logger = logging.getLogger() + try: + raise RuntimeError() + except RuntimeError: + logging.exception("just testing") + sys.stdout.seek(0) + self.assertEquals(output.getvalue(), + "ERROR:root:just testing\nGot a [RuntimeError]\n") + # Original logger output is empty + self.assert_log_lines([]) + + +class LogRecordStreamHandler(StreamRequestHandler): + + """Handler for a streaming logging request. It saves the log message in the + TCP server's 'log_output' attribute.""" + + TCP_LOG_END = "!!!END!!!" + + def handle(self): + """Handle multiple requests - each expected to be of 4-byte length, + followed by the LogRecord in pickle format. Logs the record + according to whatever policy is configured locally.""" + 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.handle_log_record(record) + + def unpickle(self, data): + return cPickle.loads(data) + + def handle_log_record(self, record): + # If the end-of-messages sentinel is seen, tell the server to + # terminate. + if self.TCP_LOG_END in record.msg: + self.server.abort = 1 + return + self.server.log_output += record.msg + "\n" + class LogRecordSocketReceiver(ThreadingTCPServer): - """ - A simple-minded TCP socket-based logging receiver suitable for test - purposes. - """ + + """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 +704,119 @@ class LogRecordSocketReceiver(ThreadingTCPServer): 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): -def banner(nm, typ): - sep = BANNER % (nm, typ) - sys.stdout.write(sep) - sys.stdout.flush() + """Test for SocketHandler objects.""" -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) + 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) + self.tcpserver = LogRecordSocketReceiver(port=0) + self.port = self.tcpserver.socket.getsockname()[1] + self.threads = [ + threading.Thread(target=self.tcpserver.serve_until_stopped)] + for thread in self.threads: + thread.start() - INF_UNDEF = logging.getLogger("INF.UNDEF") - INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF") - UNDEF = logging.getLogger("UNDEF") + 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) - GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF") - CHILD = logging.getLogger("INF.BADPARENT") + 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) - #These should log - ERR.log(logging.FATAL, nextmessage()) - ERR.error(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(LogRecordStreamHandler.TCP_LOG_END) + self.tcpserver.finished.wait(2.0) + return self.tcpserver.log_output - INF.log(logging.FATAL, nextmessage()) - INF.error(nextmessage()) - INF.warn(nextmessage()) - INF.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_UNDEF.log(logging.FATAL, nextmessage()) - INF_UNDEF.error(nextmessage()) - INF_UNDEF.warn (nextmessage()) - INF_UNDEF.info (nextmessage()) - INF_ERR.log(logging.FATAL, nextmessage()) - INF_ERR.error(nextmessage()) +class MemoryTest(BaseTest): - INF_ERR_UNDEF.log(logging.FATAL, nextmessage()) - INF_ERR_UNDEF.error(nextmessage()) + """Test memory persistence of logger objects.""" - DEB.log(logging.FATAL, nextmessage()) - DEB.error(nextmessage()) - DEB.warn (nextmessage()) - DEB.info (nextmessage()) - DEB.debug(nextmessage()) + def setUp(self): + """Create a dict to remember potentially destroyed objects.""" + BaseTest.setUp(self) + self._survivors = {} - UNDEF.log(logging.FATAL, nextmessage()) - UNDEF.error(nextmessage()) - UNDEF.warn (nextmessage()) - UNDEF.info (nextmessage()) + def _watch_for_survival(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) - GRANDCHILD.log(logging.FATAL, nextmessage()) - CHILD.log(logging.FATAL, nextmessage()) + def _assert_survival(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))) - #These should not log - ERR.warn(nextmessage()) - ERR.info(nextmessage()) - ERR.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._watch_for_survival(foo) + foo.setLevel(logging.DEBUG) + self.root_logger.debug(self.next_message()) + foo.debug(self.next_message()) + self.assert_log_lines([ + ('foo', 'DEBUG', '2'), + ]) + del foo + # foo has survived. + self._assert_survival() + # foo has retained its settings. + bar = logging.getLogger("foo") + bar.debug(self.next_message()) + self.assert_log_lines([ + ('foo', 'DEBUG', '2'), + ('foo', 'DEBUG', '3'), + ]) - INF.debug(nextmessage()) - INF_UNDEF.debug(nextmessage()) - - 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()) - - INF.info(FINISH_UP) - -def test_main_inner(): - rootLogger = logging.getLogger("") - rootLogger.setLevel(logging.DEBUG) - - 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") +# 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(): - from test import test_support, test_logging - test_support.run_doctest(test_logging) + run_unittest(BuiltinLevelsTest, BasicFilterTest, + CustomLevelsAndFiltersTest, MemoryHandlerTest, + ConfigFileTest, SocketHandlerTest, MemoryTest) -if __name__=="__main__": +if __name__ == "__main__": test_main() diff --git a/Misc/ACKS b/Misc/ACKS index 794b1f427dc..f7a08d496aa 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -524,6 +524,7 @@ Martijn Pieters François Pinard Zach Pincus Michael Piotrowski +Antoine Pitrou Michael Pomraning Iustin Pop John Popplewell diff --git a/Misc/NEWS b/Misc/NEWS index 3cd73fbf802..208c350f24f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -1419,7 +1419,7 @@ Extension Modules Tests ----- -- Refactor test_logging to use doctest. +- Refactor test_logging to use unittest. - Refactor test_profile and test_cprofile to use the same code to profile.