Home | History | Annotate | Download | only in test
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
      4 #
      5 # Permission to use, copy, modify, and distribute this software and its
      6 # documentation for any purpose and without fee is hereby granted,
      7 # provided that the above copyright notice appear in all copies and that
      8 # both that copyright notice and this permission notice appear in
      9 # supporting documentation, and that the name of Vinay Sajip
     10 # not be used in advertising or publicity pertaining to distribution
     11 # of the software without specific, written prior permission.
     12 # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     13 # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
     14 # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     15 # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     16 # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     17 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18 
     19 """Test harness for the logging module. Run all tests.
     20 
     21 Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
     22 """
     23 
     24 import logging
     25 import logging.handlers
     26 import logging.config
     27 
     28 import codecs
     29 import cPickle
     30 import cStringIO
     31 import gc
     32 import json
     33 import os
     34 import random
     35 import re
     36 import select
     37 import socket
     38 from SocketServer import ThreadingTCPServer, StreamRequestHandler
     39 import struct
     40 import sys
     41 import tempfile
     42 from test.test_support import captured_stdout, run_with_locale, run_unittest
     43 import textwrap
     44 import time
     45 import unittest
     46 import warnings
     47 import weakref
     48 try:
     49     import threading
     50 except ImportError:
     51     threading = None
     52 
     53 class BaseTest(unittest.TestCase):
     54 
     55     """Base class for logging tests."""
     56 
     57     log_format = "%(name)s -> %(levelname)s: %(message)s"
     58     expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
     59     message_num = 0
     60 
     61     def setUp(self):
     62         """Setup the default logging stream to an internal StringIO instance,
     63         so that we can examine log output as we want."""
     64         logger_dict = logging.getLogger().manager.loggerDict
     65         logging._acquireLock()
     66         try:
     67             self.saved_handlers = logging._handlers.copy()
     68             self.saved_handler_list = logging._handlerList[:]
     69             self.saved_loggers = logger_dict.copy()
     70             self.saved_level_names = logging._levelNames.copy()
     71         finally:
     72             logging._releaseLock()
     73 
     74         # Set two unused loggers: one non-ASCII and one Unicode.
     75         # This is to test correct operation when sorting existing
     76         # loggers in the configuration code. See issue 8201.
     77         logging.getLogger("\xab\xd7\xbb")
     78         logging.getLogger(u"\u013f\u00d6\u0047")
     79 
     80         self.root_logger = logging.getLogger("")
     81         self.original_logging_level = self.root_logger.getEffectiveLevel()
     82 
     83         self.stream = cStringIO.StringIO()
     84         self.root_logger.setLevel(logging.DEBUG)
     85         self.root_hdlr = logging.StreamHandler(self.stream)
     86         self.root_formatter = logging.Formatter(self.log_format)
     87         self.root_hdlr.setFormatter(self.root_formatter)
     88         self.root_logger.addHandler(self.root_hdlr)
     89 
     90     def tearDown(self):
     91         """Remove our logging stream, and restore the original logging
     92         level."""
     93         self.stream.close()
     94         self.root_logger.removeHandler(self.root_hdlr)
     95         while self.root_logger.handlers:
     96             h = self.root_logger.handlers[0]
     97             self.root_logger.removeHandler(h)
     98             h.close()
     99         self.root_logger.setLevel(self.original_logging_level)
    100         logging._acquireLock()
    101         try:
    102             logging._levelNames.clear()
    103             logging._levelNames.update(self.saved_level_names)
    104             logging._handlers.clear()
    105             logging._handlers.update(self.saved_handlers)
    106             logging._handlerList[:] = self.saved_handler_list
    107             loggerDict = logging.getLogger().manager.loggerDict
    108             loggerDict.clear()
    109             loggerDict.update(self.saved_loggers)
    110         finally:
    111             logging._releaseLock()
    112 
    113     def assert_log_lines(self, expected_values, stream=None):
    114         """Match the collected log lines against the regular expression
    115         self.expected_log_pat, and compare the extracted group values to
    116         the expected_values list of tuples."""
    117         stream = stream or self.stream
    118         pat = re.compile(self.expected_log_pat)
    119         try:
    120             stream.reset()
    121             actual_lines = stream.readlines()
    122         except AttributeError:
    123             # StringIO.StringIO lacks a reset() method.
    124             actual_lines = stream.getvalue().splitlines()
    125         self.assertEqual(len(actual_lines), len(expected_values))
    126         for actual, expected in zip(actual_lines, expected_values):
    127             match = pat.search(actual)
    128             if not match:
    129                 self.fail("Log line does not match expected pattern:\n" +
    130                             actual)
    131             self.assertEqual(tuple(match.groups()), expected)
    132         s = stream.read()
    133         if s:
    134             self.fail("Remaining output at end of log stream:\n" + s)
    135 
    136     def next_message(self):
    137         """Generate a message consisting solely of an auto-incrementing
    138         integer."""
    139         self.message_num += 1
    140         return "%d" % self.message_num
    141 
    142 
    143 class BuiltinLevelsTest(BaseTest):
    144     """Test builtin levels and their inheritance."""
    145 
    146     def test_flat(self):
    147         #Logging levels in a flat logger namespace.
    148         m = self.next_message
    149 
    150         ERR = logging.getLogger("ERR")
    151         ERR.setLevel(logging.ERROR)
    152         INF = logging.getLogger("INF")
    153         INF.setLevel(logging.INFO)
    154         DEB = logging.getLogger("DEB")
    155         DEB.setLevel(logging.DEBUG)
    156 
    157         # These should log.
    158         ERR.log(logging.CRITICAL, m())
    159         ERR.error(m())
    160 
    161         INF.log(logging.CRITICAL, m())
    162         INF.error(m())
    163         INF.warn(m())
    164         INF.info(m())
    165 
    166         DEB.log(logging.CRITICAL, m())
    167         DEB.error(m())
    168         DEB.warn (m())
    169         DEB.info (m())
    170         DEB.debug(m())
    171 
    172         # These should not log.
    173         ERR.warn(m())
    174         ERR.info(m())
    175         ERR.debug(m())
    176 
    177         INF.debug(m())
    178 
    179         self.assert_log_lines([
    180             ('ERR', 'CRITICAL', '1'),
    181             ('ERR', 'ERROR', '2'),
    182             ('INF', 'CRITICAL', '3'),
    183             ('INF', 'ERROR', '4'),
    184             ('INF', 'WARNING', '5'),
    185             ('INF', 'INFO', '6'),
    186             ('DEB', 'CRITICAL', '7'),
    187             ('DEB', 'ERROR', '8'),
    188             ('DEB', 'WARNING', '9'),
    189             ('DEB', 'INFO', '10'),
    190             ('DEB', 'DEBUG', '11'),
    191         ])
    192 
    193     def test_nested_explicit(self):
    194         # Logging levels in a nested namespace, all explicitly set.
    195         m = self.next_message
    196 
    197         INF = logging.getLogger("INF")
    198         INF.setLevel(logging.INFO)
    199         INF_ERR  = logging.getLogger("INF.ERR")
    200         INF_ERR.setLevel(logging.ERROR)
    201 
    202         # These should log.
    203         INF_ERR.log(logging.CRITICAL, m())
    204         INF_ERR.error(m())
    205 
    206         # These should not log.
    207         INF_ERR.warn(m())
    208         INF_ERR.info(m())
    209         INF_ERR.debug(m())
    210 
    211         self.assert_log_lines([
    212             ('INF.ERR', 'CRITICAL', '1'),
    213             ('INF.ERR', 'ERROR', '2'),
    214         ])
    215 
    216     def test_nested_inherited(self):
    217         #Logging levels in a nested namespace, inherited from parent loggers.
    218         m = self.next_message
    219 
    220         INF = logging.getLogger("INF")
    221         INF.setLevel(logging.INFO)
    222         INF_ERR  = logging.getLogger("INF.ERR")
    223         INF_ERR.setLevel(logging.ERROR)
    224         INF_UNDEF = logging.getLogger("INF.UNDEF")
    225         INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
    226         UNDEF = logging.getLogger("UNDEF")
    227 
    228         # These should log.
    229         INF_UNDEF.log(logging.CRITICAL, m())
    230         INF_UNDEF.error(m())
    231         INF_UNDEF.warn(m())
    232         INF_UNDEF.info(m())
    233         INF_ERR_UNDEF.log(logging.CRITICAL, m())
    234         INF_ERR_UNDEF.error(m())
    235 
    236         # These should not log.
    237         INF_UNDEF.debug(m())
    238         INF_ERR_UNDEF.warn(m())
    239         INF_ERR_UNDEF.info(m())
    240         INF_ERR_UNDEF.debug(m())
    241 
    242         self.assert_log_lines([
    243             ('INF.UNDEF', 'CRITICAL', '1'),
    244             ('INF.UNDEF', 'ERROR', '2'),
    245             ('INF.UNDEF', 'WARNING', '3'),
    246             ('INF.UNDEF', 'INFO', '4'),
    247             ('INF.ERR.UNDEF', 'CRITICAL', '5'),
    248             ('INF.ERR.UNDEF', 'ERROR', '6'),
    249         ])
    250 
    251     def test_nested_with_virtual_parent(self):
    252         # Logging levels when some parent does not exist yet.
    253         m = self.next_message
    254 
    255         INF = logging.getLogger("INF")
    256         GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
    257         CHILD = logging.getLogger("INF.BADPARENT")
    258         INF.setLevel(logging.INFO)
    259 
    260         # These should log.
    261         GRANDCHILD.log(logging.FATAL, m())
    262         GRANDCHILD.info(m())
    263         CHILD.log(logging.FATAL, m())
    264         CHILD.info(m())
    265 
    266         # These should not log.
    267         GRANDCHILD.debug(m())
    268         CHILD.debug(m())
    269 
    270         self.assert_log_lines([
    271             ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
    272             ('INF.BADPARENT.UNDEF', 'INFO', '2'),
    273             ('INF.BADPARENT', 'CRITICAL', '3'),
    274             ('INF.BADPARENT', 'INFO', '4'),
    275         ])
    276 
    277     def test_invalid_name(self):
    278         self.assertRaises(TypeError, logging.getLogger, any)
    279 
    280 class BasicFilterTest(BaseTest):
    281 
    282     """Test the bundled Filter class."""
    283 
    284     def test_filter(self):
    285         # Only messages satisfying the specified criteria pass through the
    286         #  filter.
    287         filter_ = logging.Filter("spam.eggs")
    288         handler = self.root_logger.handlers[0]
    289         try:
    290             handler.addFilter(filter_)
    291             spam = logging.getLogger("spam")
    292             spam_eggs = logging.getLogger("spam.eggs")
    293             spam_eggs_fish = logging.getLogger("spam.eggs.fish")
    294             spam_bakedbeans = logging.getLogger("spam.bakedbeans")
    295 
    296             spam.info(self.next_message())
    297             spam_eggs.info(self.next_message())  # Good.
    298             spam_eggs_fish.info(self.next_message())  # Good.
    299             spam_bakedbeans.info(self.next_message())
    300 
    301             self.assert_log_lines([
    302                 ('spam.eggs', 'INFO', '2'),
    303                 ('spam.eggs.fish', 'INFO', '3'),
    304             ])
    305         finally:
    306             handler.removeFilter(filter_)
    307 
    308 
    309 #
    310 #   First, we define our levels. There can be as many as you want - the only
    311 #     limitations are that they should be integers, the lowest should be > 0 and
    312 #   larger values mean less information being logged. If you need specific
    313 #   level values which do not fit into these limitations, you can use a
    314 #   mapping dictionary to convert between your application levels and the
    315 #   logging system.
    316 #
    317 SILENT      = 120
    318 TACITURN    = 119
    319 TERSE       = 118
    320 EFFUSIVE    = 117
    321 SOCIABLE    = 116
    322 VERBOSE     = 115
    323 TALKATIVE   = 114
    324 GARRULOUS   = 113
    325 CHATTERBOX  = 112
    326 BORING      = 111
    327 
    328 LEVEL_RANGE = range(BORING, SILENT + 1)
    329 
    330 #
    331 #   Next, we define names for our levels. You don't need to do this - in which
    332 #   case the system will use "Level n" to denote the text for the level.
    333 #
    334 my_logging_levels = {
    335     SILENT      : 'Silent',
    336     TACITURN    : 'Taciturn',
    337     TERSE       : 'Terse',
    338     EFFUSIVE    : 'Effusive',
    339     SOCIABLE    : 'Sociable',
    340     VERBOSE     : 'Verbose',
    341     TALKATIVE   : 'Talkative',
    342     GARRULOUS   : 'Garrulous',
    343     CHATTERBOX  : 'Chatterbox',
    344     BORING      : 'Boring',
    345 }
    346 
    347 class GarrulousFilter(logging.Filter):
    348 
    349     """A filter which blocks garrulous messages."""
    350 
    351     def filter(self, record):
    352         return record.levelno != GARRULOUS
    353 
    354 class VerySpecificFilter(logging.Filter):
    355 
    356     """A filter which blocks sociable and taciturn messages."""
    357 
    358     def filter(self, record):
    359         return record.levelno not in [SOCIABLE, TACITURN]
    360 
    361 
    362 class CustomLevelsAndFiltersTest(BaseTest):
    363 
    364     """Test various filtering possibilities with custom logging levels."""
    365 
    366     # Skip the logger name group.
    367     expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
    368 
    369     def setUp(self):
    370         BaseTest.setUp(self)
    371         for k, v in my_logging_levels.items():
    372             logging.addLevelName(k, v)
    373 
    374     def log_at_all_levels(self, logger):
    375         for lvl in LEVEL_RANGE:
    376             logger.log(lvl, self.next_message())
    377 
    378     def test_logger_filter(self):
    379         # Filter at logger level.
    380         self.root_logger.setLevel(VERBOSE)
    381         # Levels >= 'Verbose' are good.
    382         self.log_at_all_levels(self.root_logger)
    383         self.assert_log_lines([
    384             ('Verbose', '5'),
    385             ('Sociable', '6'),
    386             ('Effusive', '7'),
    387             ('Terse', '8'),
    388             ('Taciturn', '9'),
    389             ('Silent', '10'),
    390         ])
    391 
    392     def test_handler_filter(self):
    393         # Filter at handler level.
    394         self.root_logger.handlers[0].setLevel(SOCIABLE)
    395         try:
    396             # Levels >= 'Sociable' are good.
    397             self.log_at_all_levels(self.root_logger)
    398             self.assert_log_lines([
    399                 ('Sociable', '6'),
    400                 ('Effusive', '7'),
    401                 ('Terse', '8'),
    402                 ('Taciturn', '9'),
    403                 ('Silent', '10'),
    404             ])
    405         finally:
    406             self.root_logger.handlers[0].setLevel(logging.NOTSET)
    407 
    408     def test_specific_filters(self):
    409         # Set a specific filter object on the handler, and then add another
    410         #  filter object on the logger itself.
    411         handler = self.root_logger.handlers[0]
    412         specific_filter = None
    413         garr = GarrulousFilter()
    414         handler.addFilter(garr)
    415         try:
    416             self.log_at_all_levels(self.root_logger)
    417             first_lines = [
    418                 # Notice how 'Garrulous' is missing
    419                 ('Boring', '1'),
    420                 ('Chatterbox', '2'),
    421                 ('Talkative', '4'),
    422                 ('Verbose', '5'),
    423                 ('Sociable', '6'),
    424                 ('Effusive', '7'),
    425                 ('Terse', '8'),
    426                 ('Taciturn', '9'),
    427                 ('Silent', '10'),
    428             ]
    429             self.assert_log_lines(first_lines)
    430 
    431             specific_filter = VerySpecificFilter()
    432             self.root_logger.addFilter(specific_filter)
    433             self.log_at_all_levels(self.root_logger)
    434             self.assert_log_lines(first_lines + [
    435                 # Not only 'Garrulous' is still missing, but also 'Sociable'
    436                 # and 'Taciturn'
    437                 ('Boring', '11'),
    438                 ('Chatterbox', '12'),
    439                 ('Talkative', '14'),
    440                 ('Verbose', '15'),
    441                 ('Effusive', '17'),
    442                 ('Terse', '18'),
    443                 ('Silent', '20'),
    444         ])
    445         finally:
    446             if specific_filter:
    447                 self.root_logger.removeFilter(specific_filter)
    448             handler.removeFilter(garr)
    449 
    450 
    451 class MemoryHandlerTest(BaseTest):
    452 
    453     """Tests for the MemoryHandler."""
    454 
    455     # Do not bother with a logger name group.
    456     expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
    457 
    458     def setUp(self):
    459         BaseTest.setUp(self)
    460         self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
    461                                                         self.root_hdlr)
    462         self.mem_logger = logging.getLogger('mem')
    463         self.mem_logger.propagate = 0
    464         self.mem_logger.addHandler(self.mem_hdlr)
    465 
    466     def tearDown(self):
    467         self.mem_hdlr.close()
    468         BaseTest.tearDown(self)
    469 
    470     def test_flush(self):
    471         # The memory handler flushes to its target handler based on specific
    472         #  criteria (message count and message level).
    473         self.mem_logger.debug(self.next_message())
    474         self.assert_log_lines([])
    475         self.mem_logger.info(self.next_message())
    476         self.assert_log_lines([])
    477         # This will flush because the level is >= logging.WARNING
    478         self.mem_logger.warn(self.next_message())
    479         lines = [
    480             ('DEBUG', '1'),
    481             ('INFO', '2'),
    482             ('WARNING', '3'),
    483         ]
    484         self.assert_log_lines(lines)
    485         for n in (4, 14):
    486             for i in range(9):
    487                 self.mem_logger.debug(self.next_message())
    488             self.assert_log_lines(lines)
    489             # This will flush because it's the 10th message since the last
    490             #  flush.
    491             self.mem_logger.debug(self.next_message())
    492             lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
    493             self.assert_log_lines(lines)
    494 
    495         self.mem_logger.debug(self.next_message())
    496         self.assert_log_lines(lines)
    497 
    498 
    499 class ExceptionFormatter(logging.Formatter):
    500     """A special exception formatter."""
    501     def formatException(self, ei):
    502         return "Got a [%s]" % ei[0].__name__
    503 
    504 
    505 class ConfigFileTest(BaseTest):
    506 
    507     """Reading logging config from a .ini-style config file."""
    508 
    509     expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
    510 
    511     # config0 is a standard configuration.
    512     config0 = """
    513     [loggers]
    514     keys=root
    515 
    516     [handlers]
    517     keys=hand1
    518 
    519     [formatters]
    520     keys=form1
    521 
    522     [logger_root]
    523     level=WARNING
    524     handlers=hand1
    525 
    526     [handler_hand1]
    527     class=StreamHandler
    528     level=NOTSET
    529     formatter=form1
    530     args=(sys.stdout,)
    531 
    532     [formatter_form1]
    533     format=%(levelname)s ++ %(message)s
    534     datefmt=
    535     """
    536 
    537     # config1 adds a little to the standard configuration.
    538     config1 = """
    539     [loggers]
    540     keys=root,parser
    541 
    542     [handlers]
    543     keys=hand1
    544 
    545     [formatters]
    546     keys=form1
    547 
    548     [logger_root]
    549     level=WARNING
    550     handlers=
    551 
    552     [logger_parser]
    553     level=DEBUG
    554     handlers=hand1
    555     propagate=1
    556     qualname=compiler.parser
    557 
    558     [handler_hand1]
    559     class=StreamHandler
    560     level=NOTSET
    561     formatter=form1
    562     args=(sys.stdout,)
    563 
    564     [formatter_form1]
    565     format=%(levelname)s ++ %(message)s
    566     datefmt=
    567     """
    568 
    569     # config1a moves the handler to the root.
    570     config1a = """
    571     [loggers]
    572     keys=root,parser
    573 
    574     [handlers]
    575     keys=hand1
    576 
    577     [formatters]
    578     keys=form1
    579 
    580     [logger_root]
    581     level=WARNING
    582     handlers=hand1
    583 
    584     [logger_parser]
    585     level=DEBUG
    586     handlers=
    587     propagate=1
    588     qualname=compiler.parser
    589 
    590     [handler_hand1]
    591     class=StreamHandler
    592     level=NOTSET
    593     formatter=form1
    594     args=(sys.stdout,)
    595 
    596     [formatter_form1]
    597     format=%(levelname)s ++ %(message)s
    598     datefmt=
    599     """
    600 
    601     # config2 has a subtle configuration error that should be reported
    602     config2 = config1.replace("sys.stdout", "sys.stbout")
    603 
    604     # config3 has a less subtle configuration error
    605     config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
    606 
    607     # config4 specifies a custom formatter class to be loaded
    608     config4 = """
    609     [loggers]
    610     keys=root
    611 
    612     [handlers]
    613     keys=hand1
    614 
    615     [formatters]
    616     keys=form1
    617 
    618     [logger_root]
    619     level=NOTSET
    620     handlers=hand1
    621 
    622     [handler_hand1]
    623     class=StreamHandler
    624     level=NOTSET
    625     formatter=form1
    626     args=(sys.stdout,)
    627 
    628     [formatter_form1]
    629     class=""" + __name__ + """.ExceptionFormatter
    630     format=%(levelname)s:%(name)s:%(message)s
    631     datefmt=
    632     """
    633 
    634     # config5 specifies a custom handler class to be loaded
    635     config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
    636 
    637     # config6 uses ', ' delimiters in the handlers and formatters sections
    638     config6 = """
    639     [loggers]
    640     keys=root,parser
    641 
    642     [handlers]
    643     keys=hand1, hand2
    644 
    645     [formatters]
    646     keys=form1, form2
    647 
    648     [logger_root]
    649     level=WARNING
    650     handlers=
    651 
    652     [logger_parser]
    653     level=DEBUG
    654     handlers=hand1
    655     propagate=1
    656     qualname=compiler.parser
    657 
    658     [handler_hand1]
    659     class=StreamHandler
    660     level=NOTSET
    661     formatter=form1
    662     args=(sys.stdout,)
    663 
    664     [handler_hand2]
    665     class=StreamHandler
    666     level=NOTSET
    667     formatter=form1
    668     args=(sys.stderr,)
    669 
    670     [formatter_form1]
    671     format=%(levelname)s ++ %(message)s
    672     datefmt=
    673 
    674     [formatter_form2]
    675     format=%(message)s
    676     datefmt=
    677     """
    678 
    679     # config7 adds a compiler logger.
    680     config7 = """
    681     [loggers]
    682     keys=root,parser,compiler
    683 
    684     [handlers]
    685     keys=hand1
    686 
    687     [formatters]
    688     keys=form1
    689 
    690     [logger_root]
    691     level=WARNING
    692     handlers=hand1
    693 
    694     [logger_compiler]
    695     level=DEBUG
    696     handlers=
    697     propagate=1
    698     qualname=compiler
    699 
    700     [logger_parser]
    701     level=DEBUG
    702     handlers=
    703     propagate=1
    704     qualname=compiler.parser
    705 
    706     [handler_hand1]
    707     class=StreamHandler
    708     level=NOTSET
    709     formatter=form1
    710     args=(sys.stdout,)
    711 
    712     [formatter_form1]
    713     format=%(levelname)s ++ %(message)s
    714     datefmt=
    715     """
    716 
    717     disable_test = """
    718     [loggers]
    719     keys=root
    720 
    721     [handlers]
    722     keys=screen
    723 
    724     [formatters]
    725     keys=
    726 
    727     [logger_root]
    728     level=DEBUG
    729     handlers=screen
    730 
    731     [handler_screen]
    732     level=DEBUG
    733     class=StreamHandler
    734     args=(sys.stdout,)
    735     formatter=
    736     """
    737 
    738     def apply_config(self, conf, **kwargs):
    739         file = cStringIO.StringIO(textwrap.dedent(conf))
    740         logging.config.fileConfig(file, **kwargs)
    741 
    742     def test_config0_ok(self):
    743         # A simple config file which overrides the default settings.
    744         with captured_stdout() as output:
    745             self.apply_config(self.config0)
    746             logger = logging.getLogger()
    747             # Won't output anything
    748             logger.info(self.next_message())
    749             # Outputs a message
    750             logger.error(self.next_message())
    751             self.assert_log_lines([
    752                 ('ERROR', '2'),
    753             ], stream=output)
    754             # Original logger output is empty.
    755             self.assert_log_lines([])
    756 
    757     def test_config1_ok(self, config=config1):
    758         # A config file defining a sub-parser as well.
    759         with captured_stdout() as output:
    760             self.apply_config(config)
    761             logger = logging.getLogger("compiler.parser")
    762             # Both will output a message
    763             logger.info(self.next_message())
    764             logger.error(self.next_message())
    765             self.assert_log_lines([
    766                 ('INFO', '1'),
    767                 ('ERROR', '2'),
    768             ], stream=output)
    769             # Original logger output is empty.
    770             self.assert_log_lines([])
    771 
    772     def test_config2_failure(self):
    773         # A simple config file which overrides the default settings.
    774         self.assertRaises(StandardError, self.apply_config, self.config2)
    775 
    776     def test_config3_failure(self):
    777         # A simple config file which overrides the default settings.
    778         self.assertRaises(StandardError, self.apply_config, self.config3)
    779 
    780     def test_config4_ok(self):
    781         # A config file specifying a custom formatter class.
    782         with captured_stdout() as output:
    783             self.apply_config(self.config4)
    784             logger = logging.getLogger()
    785             try:
    786                 raise RuntimeError()
    787             except RuntimeError:
    788                 logging.exception("just testing")
    789             sys.stdout.seek(0)
    790             self.assertEqual(output.getvalue(),
    791                 "ERROR:root:just testing\nGot a [RuntimeError]\n")
    792             # Original logger output is empty
    793             self.assert_log_lines([])
    794 
    795     def test_config5_ok(self):
    796         self.test_config1_ok(config=self.config5)
    797 
    798     def test_config6_ok(self):
    799         self.test_config1_ok(config=self.config6)
    800 
    801     def test_config7_ok(self):
    802         with captured_stdout() as output:
    803             self.apply_config(self.config1a)
    804             logger = logging.getLogger("compiler.parser")
    805             # See issue #11424. compiler-hyphenated sorts
    806             # between compiler and compiler.xyz and this
    807             # was preventing compiler.xyz from being included
    808             # in the child loggers of compiler because of an
    809             # overzealous loop termination condition.
    810             hyphenated = logging.getLogger('compiler-hyphenated')
    811             # All will output a message
    812             logger.info(self.next_message())
    813             logger.error(self.next_message())
    814             hyphenated.critical(self.next_message())
    815             self.assert_log_lines([
    816                 ('INFO', '1'),
    817                 ('ERROR', '2'),
    818                 ('CRITICAL', '3'),
    819             ], stream=output)
    820             # Original logger output is empty.
    821             self.assert_log_lines([])
    822         with captured_stdout() as output:
    823             self.apply_config(self.config7)
    824             logger = logging.getLogger("compiler.parser")
    825             self.assertFalse(logger.disabled)
    826             # Both will output a message
    827             logger.info(self.next_message())
    828             logger.error(self.next_message())
    829             logger = logging.getLogger("compiler.lexer")
    830             # Both will output a message
    831             logger.info(self.next_message())
    832             logger.error(self.next_message())
    833             # Will not appear
    834             hyphenated.critical(self.next_message())
    835             self.assert_log_lines([
    836                 ('INFO', '4'),
    837                 ('ERROR', '5'),
    838                 ('INFO', '6'),
    839                 ('ERROR', '7'),
    840             ], stream=output)
    841             # Original logger output is empty.
    842             self.assert_log_lines([])
    843 
    844     def test_logger_disabling(self):
    845         self.apply_config(self.disable_test)
    846         logger = logging.getLogger('foo')
    847         self.assertFalse(logger.disabled)
    848         self.apply_config(self.disable_test)
    849         self.assertTrue(logger.disabled)
    850         self.apply_config(self.disable_test, disable_existing_loggers=False)
    851         self.assertFalse(logger.disabled)
    852 
    853 class LogRecordStreamHandler(StreamRequestHandler):
    854 
    855     """Handler for a streaming logging request. It saves the log message in the
    856     TCP server's 'log_output' attribute."""
    857 
    858     TCP_LOG_END = "!!!END!!!"
    859 
    860     def handle(self):
    861         """Handle multiple requests - each expected to be of 4-byte length,
    862         followed by the LogRecord in pickle format. Logs the record
    863         according to whatever policy is configured locally."""
    864         while True:
    865             chunk = self.connection.recv(4)
    866             if len(chunk) < 4:
    867                 break
    868             slen = struct.unpack(">L", chunk)[0]
    869             chunk = self.connection.recv(slen)
    870             while len(chunk) < slen:
    871                 chunk = chunk + self.connection.recv(slen - len(chunk))
    872             obj = self.unpickle(chunk)
    873             record = logging.makeLogRecord(obj)
    874             self.handle_log_record(record)
    875 
    876     def unpickle(self, data):
    877         return cPickle.loads(data)
    878 
    879     def handle_log_record(self, record):
    880         # If the end-of-messages sentinel is seen, tell the server to
    881         #  terminate.
    882         if self.TCP_LOG_END in record.msg:
    883             self.server.abort = 1
    884             return
    885         self.server.log_output += record.msg + "\n"
    886 
    887 
    888 class LogRecordSocketReceiver(ThreadingTCPServer):
    889 
    890     """A simple-minded TCP socket-based logging receiver suitable for test
    891     purposes."""
    892 
    893     allow_reuse_address = 1
    894     log_output = ""
    895 
    896     def __init__(self, host='localhost',
    897                              port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
    898                      handler=LogRecordStreamHandler):
    899         ThreadingTCPServer.__init__(self, (host, port), handler)
    900         self.abort = False
    901         self.timeout = 0.1
    902         self.finished = threading.Event()
    903 
    904     def serve_until_stopped(self):
    905         while not self.abort:
    906             rd, wr, ex = select.select([self.socket.fileno()], [], [],
    907                                        self.timeout)
    908             if rd:
    909                 self.handle_request()
    910         # Notify the main thread that we're about to exit
    911         self.finished.set()
    912         # close the listen socket
    913         self.server_close()
    914 
    915 
    916 @unittest.skipUnless(threading, 'Threading required for this test.')
    917 class SocketHandlerTest(BaseTest):
    918 
    919     """Test for SocketHandler objects."""
    920 
    921     def setUp(self):
    922         """Set up a TCP server to receive log messages, and a SocketHandler
    923         pointing to that server's address and port."""
    924         BaseTest.setUp(self)
    925         self.tcpserver = LogRecordSocketReceiver(port=0)
    926         self.port = self.tcpserver.socket.getsockname()[1]
    927         self.threads = [
    928                 threading.Thread(target=self.tcpserver.serve_until_stopped)]
    929         for thread in self.threads:
    930             thread.start()
    931 
    932         self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
    933         self.sock_hdlr.setFormatter(self.root_formatter)
    934         self.root_logger.removeHandler(self.root_logger.handlers[0])
    935         self.root_logger.addHandler(self.sock_hdlr)
    936 
    937     def tearDown(self):
    938         """Shutdown the TCP server."""
    939         try:
    940             self.tcpserver.abort = True
    941             del self.tcpserver
    942             self.root_logger.removeHandler(self.sock_hdlr)
    943             self.sock_hdlr.close()
    944             for thread in self.threads:
    945                 thread.join(2.0)
    946         finally:
    947             BaseTest.tearDown(self)
    948 
    949     def get_output(self):
    950         """Get the log output as received by the TCP server."""
    951         # Signal the TCP receiver and wait for it to terminate.
    952         self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
    953         self.tcpserver.finished.wait(2.0)
    954         return self.tcpserver.log_output
    955 
    956     def test_output(self):
    957         # The log message sent to the SocketHandler is properly received.
    958         logger = logging.getLogger("tcp")
    959         logger.error("spam")
    960         logger.debug("eggs")
    961         self.assertEqual(self.get_output(), "spam\neggs\n")
    962 
    963 
    964 class MemoryTest(BaseTest):
    965 
    966     """Test memory persistence of logger objects."""
    967 
    968     def setUp(self):
    969         """Create a dict to remember potentially destroyed objects."""
    970         BaseTest.setUp(self)
    971         self._survivors = {}
    972 
    973     def _watch_for_survival(self, *args):
    974         """Watch the given objects for survival, by creating weakrefs to
    975         them."""
    976         for obj in args:
    977             key = id(obj), repr(obj)
    978             self._survivors[key] = weakref.ref(obj)
    979 
    980     def _assertTruesurvival(self):
    981         """Assert that all objects watched for survival have survived."""
    982         # Trigger cycle breaking.
    983         gc.collect()
    984         dead = []
    985         for (id_, repr_), ref in self._survivors.items():
    986             if ref() is None:
    987                 dead.append(repr_)
    988         if dead:
    989             self.fail("%d objects should have survived "
    990                 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
    991 
    992     def test_persistent_loggers(self):
    993         # Logger objects are persistent and retain their configuration, even
    994         #  if visible references are destroyed.
    995         self.root_logger.setLevel(logging.INFO)
    996         foo = logging.getLogger("foo")
    997         self._watch_for_survival(foo)
    998         foo.setLevel(logging.DEBUG)
    999         self.root_logger.debug(self.next_message())
   1000         foo.debug(self.next_message())
   1001         self.assert_log_lines([
   1002             ('foo', 'DEBUG', '2'),
   1003         ])
   1004         del foo
   1005         # foo has survived.
   1006         self._assertTruesurvival()
   1007         # foo has retained its settings.
   1008         bar = logging.getLogger("foo")
   1009         bar.debug(self.next_message())
   1010         self.assert_log_lines([
   1011             ('foo', 'DEBUG', '2'),
   1012             ('foo', 'DEBUG', '3'),
   1013         ])
   1014 
   1015 
   1016 class EncodingTest(BaseTest):
   1017     def test_encoding_plain_file(self):
   1018         # In Python 2.x, a plain file object is treated as having no encoding.
   1019         log = logging.getLogger("test")
   1020         fn = tempfile.mktemp(".log")
   1021         # the non-ascii data we write to the log.
   1022         data = "foo\x80"
   1023         try:
   1024             handler = logging.FileHandler(fn)
   1025             log.addHandler(handler)
   1026             try:
   1027                 # write non-ascii data to the log.
   1028                 log.warning(data)
   1029             finally:
   1030                 log.removeHandler(handler)
   1031                 handler.close()
   1032             # check we wrote exactly those bytes, ignoring trailing \n etc
   1033             f = open(fn)
   1034             try:
   1035                 self.assertEqual(f.read().rstrip(), data)
   1036             finally:
   1037                 f.close()
   1038         finally:
   1039             if os.path.isfile(fn):
   1040                 os.remove(fn)
   1041 
   1042     def test_encoding_cyrillic_unicode(self):
   1043         log = logging.getLogger("test")
   1044         #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
   1045         message = u'\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
   1046         #Ensure it's written in a Cyrillic encoding
   1047         writer_class = codecs.getwriter('cp1251')
   1048         writer_class.encoding = 'cp1251'
   1049         stream = cStringIO.StringIO()
   1050         writer = writer_class(stream, 'strict')
   1051         handler = logging.StreamHandler(writer)
   1052         log.addHandler(handler)
   1053         try:
   1054             log.warning(message)
   1055         finally:
   1056             log.removeHandler(handler)
   1057             handler.close()
   1058         # check we wrote exactly those bytes, ignoring trailing \n etc
   1059         s = stream.getvalue()
   1060         #Compare against what the data should be when encoded in CP-1251
   1061         self.assertEqual(s, '\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
   1062 
   1063 
   1064 class WarningsTest(BaseTest):
   1065 
   1066     def test_warnings(self):
   1067         with warnings.catch_warnings():
   1068             logging.captureWarnings(True)
   1069             try:
   1070                 warnings.filterwarnings("always", category=UserWarning)
   1071                 file = cStringIO.StringIO()
   1072                 h = logging.StreamHandler(file)
   1073                 logger = logging.getLogger("py.warnings")
   1074                 logger.addHandler(h)
   1075                 warnings.warn("I'm warning you...")
   1076                 logger.removeHandler(h)
   1077                 s = file.getvalue()
   1078                 h.close()
   1079                 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
   1080 
   1081                 #See if an explicit file uses the original implementation
   1082                 file = cStringIO.StringIO()
   1083                 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
   1084                                         file, "Dummy line")
   1085                 s = file.getvalue()
   1086                 file.close()
   1087                 self.assertEqual(s,
   1088                         "dummy.py:42: UserWarning: Explicit\n  Dummy line\n")
   1089             finally:
   1090                 logging.captureWarnings(False)
   1091 
   1092 
   1093 def formatFunc(format, datefmt=None):
   1094     return logging.Formatter(format, datefmt)
   1095 
   1096 def handlerFunc():
   1097     return logging.StreamHandler()
   1098 
   1099 class CustomHandler(logging.StreamHandler):
   1100     pass
   1101 
   1102 class ConfigDictTest(BaseTest):
   1103 
   1104     """Reading logging config from a dictionary."""
   1105 
   1106     expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
   1107 
   1108     # config0 is a standard configuration.
   1109     config0 = {
   1110         'version': 1,
   1111         'formatters': {
   1112             'form1' : {
   1113                 'format' : '%(levelname)s ++ %(message)s',
   1114             },
   1115         },
   1116         'handlers' : {
   1117             'hand1' : {
   1118                 'class' : 'logging.StreamHandler',
   1119                 'formatter' : 'form1',
   1120                 'level' : 'NOTSET',
   1121                 'stream'  : 'ext://sys.stdout',
   1122             },
   1123         },
   1124         'root' : {
   1125             'level' : 'WARNING',
   1126             'handlers' : ['hand1'],
   1127         },
   1128     }
   1129 
   1130     # config1 adds a little to the standard configuration.
   1131     config1 = {
   1132         'version': 1,
   1133         'formatters': {
   1134             'form1' : {
   1135                 'format' : '%(levelname)s ++ %(message)s',
   1136             },
   1137         },
   1138         'handlers' : {
   1139             'hand1' : {
   1140                 'class' : 'logging.StreamHandler',
   1141                 'formatter' : 'form1',
   1142                 'level' : 'NOTSET',
   1143                 'stream'  : 'ext://sys.stdout',
   1144             },
   1145         },
   1146         'loggers' : {
   1147             'compiler.parser' : {
   1148                 'level' : 'DEBUG',
   1149                 'handlers' : ['hand1'],
   1150             },
   1151         },
   1152         'root' : {
   1153             'level' : 'WARNING',
   1154         },
   1155     }
   1156 
   1157     # config2 has a subtle configuration error that should be reported
   1158     config2 = {
   1159         'version': 1,
   1160         'formatters': {
   1161             'form1' : {
   1162                 'format' : '%(levelname)s ++ %(message)s',
   1163             },
   1164         },
   1165         'handlers' : {
   1166             'hand1' : {
   1167                 'class' : 'logging.StreamHandler',
   1168                 'formatter' : 'form1',
   1169                 'level' : 'NOTSET',
   1170                 'stream'  : 'ext://sys.stdbout',
   1171             },
   1172         },
   1173         'loggers' : {
   1174             'compiler.parser' : {
   1175                 'level' : 'DEBUG',
   1176                 'handlers' : ['hand1'],
   1177             },
   1178         },
   1179         'root' : {
   1180             'level' : 'WARNING',
   1181         },
   1182     }
   1183 
   1184     #As config1 but with a misspelt level on a handler
   1185     config2a = {
   1186         'version': 1,
   1187         'formatters': {
   1188             'form1' : {
   1189                 'format' : '%(levelname)s ++ %(message)s',
   1190             },
   1191         },
   1192         'handlers' : {
   1193             'hand1' : {
   1194                 'class' : 'logging.StreamHandler',
   1195                 'formatter' : 'form1',
   1196                 'level' : 'NTOSET',
   1197                 'stream'  : 'ext://sys.stdout',
   1198             },
   1199         },
   1200         'loggers' : {
   1201             'compiler.parser' : {
   1202                 'level' : 'DEBUG',
   1203                 'handlers' : ['hand1'],
   1204             },
   1205         },
   1206         'root' : {
   1207             'level' : 'WARNING',
   1208         },
   1209     }
   1210 
   1211 
   1212     #As config1 but with a misspelt level on a logger
   1213     config2b = {
   1214         'version': 1,
   1215         'formatters': {
   1216             'form1' : {
   1217                 'format' : '%(levelname)s ++ %(message)s',
   1218             },
   1219         },
   1220         'handlers' : {
   1221             'hand1' : {
   1222                 'class' : 'logging.StreamHandler',
   1223                 'formatter' : 'form1',
   1224                 'level' : 'NOTSET',
   1225                 'stream'  : 'ext://sys.stdout',
   1226             },
   1227         },
   1228         'loggers' : {
   1229             'compiler.parser' : {
   1230                 'level' : 'DEBUG',
   1231                 'handlers' : ['hand1'],
   1232             },
   1233         },
   1234         'root' : {
   1235             'level' : 'WRANING',
   1236         },
   1237     }
   1238 
   1239     # config3 has a less subtle configuration error
   1240     config3 = {
   1241         'version': 1,
   1242         'formatters': {
   1243             'form1' : {
   1244                 'format' : '%(levelname)s ++ %(message)s',
   1245             },
   1246         },
   1247         'handlers' : {
   1248             'hand1' : {
   1249                 'class' : 'logging.StreamHandler',
   1250                 'formatter' : 'misspelled_name',
   1251                 'level' : 'NOTSET',
   1252                 'stream'  : 'ext://sys.stdout',
   1253             },
   1254         },
   1255         'loggers' : {
   1256             'compiler.parser' : {
   1257                 'level' : 'DEBUG',
   1258                 'handlers' : ['hand1'],
   1259             },
   1260         },
   1261         'root' : {
   1262             'level' : 'WARNING',
   1263         },
   1264     }
   1265 
   1266     # config4 specifies a custom formatter class to be loaded
   1267     config4 = {
   1268         'version': 1,
   1269         'formatters': {
   1270             'form1' : {
   1271                 '()' : __name__ + '.ExceptionFormatter',
   1272                 'format' : '%(levelname)s:%(name)s:%(message)s',
   1273             },
   1274         },
   1275         'handlers' : {
   1276             'hand1' : {
   1277                 'class' : 'logging.StreamHandler',
   1278                 'formatter' : 'form1',
   1279                 'level' : 'NOTSET',
   1280                 'stream'  : 'ext://sys.stdout',
   1281             },
   1282         },
   1283         'root' : {
   1284             'level' : 'NOTSET',
   1285                 'handlers' : ['hand1'],
   1286         },
   1287     }
   1288 
   1289     # As config4 but using an actual callable rather than a string
   1290     config4a = {
   1291         'version': 1,
   1292         'formatters': {
   1293             'form1' : {
   1294                 '()' : ExceptionFormatter,
   1295                 'format' : '%(levelname)s:%(name)s:%(message)s',
   1296             },
   1297             'form2' : {
   1298                 '()' : __name__ + '.formatFunc',
   1299                 'format' : '%(levelname)s:%(name)s:%(message)s',
   1300             },
   1301             'form3' : {
   1302                 '()' : formatFunc,
   1303                 'format' : '%(levelname)s:%(name)s:%(message)s',
   1304             },
   1305         },
   1306         'handlers' : {
   1307             'hand1' : {
   1308                 'class' : 'logging.StreamHandler',
   1309                 'formatter' : 'form1',
   1310                 'level' : 'NOTSET',
   1311                 'stream'  : 'ext://sys.stdout',
   1312             },
   1313             'hand2' : {
   1314                 '()' : handlerFunc,
   1315             },
   1316         },
   1317         'root' : {
   1318             'level' : 'NOTSET',
   1319                 'handlers' : ['hand1'],
   1320         },
   1321     }
   1322 
   1323     # config5 specifies a custom handler class to be loaded
   1324     config5 = {
   1325         'version': 1,
   1326         'formatters': {
   1327             'form1' : {
   1328                 'format' : '%(levelname)s ++ %(message)s',
   1329             },
   1330         },
   1331         'handlers' : {
   1332             'hand1' : {
   1333                 'class' : __name__ + '.CustomHandler',
   1334                 'formatter' : 'form1',
   1335                 'level' : 'NOTSET',
   1336                 'stream'  : 'ext://sys.stdout',
   1337             },
   1338         },
   1339         'loggers' : {
   1340             'compiler.parser' : {
   1341                 'level' : 'DEBUG',
   1342                 'handlers' : ['hand1'],
   1343             },
   1344         },
   1345         'root' : {
   1346             'level' : 'WARNING',
   1347         },
   1348     }
   1349 
   1350     # config6 specifies a custom handler class to be loaded
   1351     # but has bad arguments
   1352     config6 = {
   1353         'version': 1,
   1354         'formatters': {
   1355             'form1' : {
   1356                 'format' : '%(levelname)s ++ %(message)s',
   1357             },
   1358         },
   1359         'handlers' : {
   1360             'hand1' : {
   1361                 'class' : __name__ + '.CustomHandler',
   1362                 'formatter' : 'form1',
   1363                 'level' : 'NOTSET',
   1364                 'stream'  : 'ext://sys.stdout',
   1365                 '9' : 'invalid parameter name',
   1366             },
   1367         },
   1368         'loggers' : {
   1369             'compiler.parser' : {
   1370                 'level' : 'DEBUG',
   1371                 'handlers' : ['hand1'],
   1372             },
   1373         },
   1374         'root' : {
   1375             'level' : 'WARNING',
   1376         },
   1377     }
   1378 
   1379     #config 7 does not define compiler.parser but defines compiler.lexer
   1380     #so compiler.parser should be disabled after applying it
   1381     config7 = {
   1382         'version': 1,
   1383         'formatters': {
   1384             'form1' : {
   1385                 'format' : '%(levelname)s ++ %(message)s',
   1386             },
   1387         },
   1388         'handlers' : {
   1389             'hand1' : {
   1390                 'class' : 'logging.StreamHandler',
   1391                 'formatter' : 'form1',
   1392                 'level' : 'NOTSET',
   1393                 'stream'  : 'ext://sys.stdout',
   1394             },
   1395         },
   1396         'loggers' : {
   1397             'compiler.lexer' : {
   1398                 'level' : 'DEBUG',
   1399                 'handlers' : ['hand1'],
   1400             },
   1401         },
   1402         'root' : {
   1403             'level' : 'WARNING',
   1404         },
   1405     }
   1406 
   1407     config8 = {
   1408         'version': 1,
   1409         'disable_existing_loggers' : False,
   1410         'formatters': {
   1411             'form1' : {
   1412                 'format' : '%(levelname)s ++ %(message)s',
   1413             },
   1414         },
   1415         'handlers' : {
   1416             'hand1' : {
   1417                 'class' : 'logging.StreamHandler',
   1418                 'formatter' : 'form1',
   1419                 'level' : 'NOTSET',
   1420                 'stream'  : 'ext://sys.stdout',
   1421             },
   1422         },
   1423         'loggers' : {
   1424             'compiler' : {
   1425                 'level' : 'DEBUG',
   1426                 'handlers' : ['hand1'],
   1427             },
   1428             'compiler.lexer' : {
   1429             },
   1430         },
   1431         'root' : {
   1432             'level' : 'WARNING',
   1433         },
   1434     }
   1435 
   1436     config9 = {
   1437         'version': 1,
   1438         'formatters': {
   1439             'form1' : {
   1440                 'format' : '%(levelname)s ++ %(message)s',
   1441             },
   1442         },
   1443         'handlers' : {
   1444             'hand1' : {
   1445                 'class' : 'logging.StreamHandler',
   1446                 'formatter' : 'form1',
   1447                 'level' : 'WARNING',
   1448                 'stream'  : 'ext://sys.stdout',
   1449             },
   1450         },
   1451         'loggers' : {
   1452             'compiler.parser' : {
   1453                 'level' : 'WARNING',
   1454                 'handlers' : ['hand1'],
   1455             },
   1456         },
   1457         'root' : {
   1458             'level' : 'NOTSET',
   1459         },
   1460     }
   1461 
   1462     config9a = {
   1463         'version': 1,
   1464         'incremental' : True,
   1465         'handlers' : {
   1466             'hand1' : {
   1467                 'level' : 'WARNING',
   1468             },
   1469         },
   1470         'loggers' : {
   1471             'compiler.parser' : {
   1472                 'level' : 'INFO',
   1473             },
   1474         },
   1475     }
   1476 
   1477     config9b = {
   1478         'version': 1,
   1479         'incremental' : True,
   1480         'handlers' : {
   1481             'hand1' : {
   1482                 'level' : 'INFO',
   1483             },
   1484         },
   1485         'loggers' : {
   1486             'compiler.parser' : {
   1487                 'level' : 'INFO',
   1488             },
   1489         },
   1490     }
   1491 
   1492     #As config1 but with a filter added
   1493     config10 = {
   1494         'version': 1,
   1495         'formatters': {
   1496             'form1' : {
   1497                 'format' : '%(levelname)s ++ %(message)s',
   1498             },
   1499         },
   1500         'filters' : {
   1501             'filt1' : {
   1502                 'name' : 'compiler.parser',
   1503             },
   1504         },
   1505         'handlers' : {
   1506             'hand1' : {
   1507                 'class' : 'logging.StreamHandler',
   1508                 'formatter' : 'form1',
   1509                 'level' : 'NOTSET',
   1510                 'stream'  : 'ext://sys.stdout',
   1511                 'filters' : ['filt1'],
   1512             },
   1513         },
   1514         'loggers' : {
   1515             'compiler.parser' : {
   1516                 'level' : 'DEBUG',
   1517                 'filters' : ['filt1'],
   1518             },
   1519         },
   1520         'root' : {
   1521             'level' : 'WARNING',
   1522             'handlers' : ['hand1'],
   1523         },
   1524     }
   1525 
   1526     #As config1 but using cfg:// references
   1527     config11 = {
   1528         'version': 1,
   1529         'true_formatters': {
   1530             'form1' : {
   1531                 'format' : '%(levelname)s ++ %(message)s',
   1532             },
   1533         },
   1534         'handler_configs': {
   1535             'hand1' : {
   1536                 'class' : 'logging.StreamHandler',
   1537                 'formatter' : 'form1',
   1538                 'level' : 'NOTSET',
   1539                 'stream'  : 'ext://sys.stdout',
   1540             },
   1541         },
   1542         'formatters' : 'cfg://true_formatters',
   1543         'handlers' : {
   1544             'hand1' : 'cfg://handler_configs[hand1]',
   1545         },
   1546         'loggers' : {
   1547             'compiler.parser' : {
   1548                 'level' : 'DEBUG',
   1549                 'handlers' : ['hand1'],
   1550             },
   1551         },
   1552         'root' : {
   1553             'level' : 'WARNING',
   1554         },
   1555     }
   1556 
   1557     #As config11 but missing the version key
   1558     config12 = {
   1559         'true_formatters': {
   1560             'form1' : {
   1561                 'format' : '%(levelname)s ++ %(message)s',
   1562             },
   1563         },
   1564         'handler_configs': {
   1565             'hand1' : {
   1566                 'class' : 'logging.StreamHandler',
   1567                 'formatter' : 'form1',
   1568                 'level' : 'NOTSET',
   1569                 'stream'  : 'ext://sys.stdout',
   1570             },
   1571         },
   1572         'formatters' : 'cfg://true_formatters',
   1573         'handlers' : {
   1574             'hand1' : 'cfg://handler_configs[hand1]',
   1575         },
   1576         'loggers' : {
   1577             'compiler.parser' : {
   1578                 'level' : 'DEBUG',
   1579                 'handlers' : ['hand1'],
   1580             },
   1581         },
   1582         'root' : {
   1583             'level' : 'WARNING',
   1584         },
   1585     }
   1586 
   1587     #As config11 but using an unsupported version
   1588     config13 = {
   1589         'version': 2,
   1590         'true_formatters': {
   1591             'form1' : {
   1592                 'format' : '%(levelname)s ++ %(message)s',
   1593             },
   1594         },
   1595         'handler_configs': {
   1596             'hand1' : {
   1597                 'class' : 'logging.StreamHandler',
   1598                 'formatter' : 'form1',
   1599                 'level' : 'NOTSET',
   1600                 'stream'  : 'ext://sys.stdout',
   1601             },
   1602         },
   1603         'formatters' : 'cfg://true_formatters',
   1604         'handlers' : {
   1605             'hand1' : 'cfg://handler_configs[hand1]',
   1606         },
   1607         'loggers' : {
   1608             'compiler.parser' : {
   1609                 'level' : 'DEBUG',
   1610                 'handlers' : ['hand1'],
   1611             },
   1612         },
   1613         'root' : {
   1614             'level' : 'WARNING',
   1615         },
   1616     }
   1617 
   1618     out_of_order = {
   1619         "version": 1,
   1620         "formatters": {
   1621             "mySimpleFormatter": {
   1622                 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s"
   1623             }
   1624         },
   1625         "handlers": {
   1626             "fileGlobal": {
   1627                 "class": "logging.StreamHandler",
   1628                 "level": "DEBUG",
   1629                 "formatter": "mySimpleFormatter"
   1630             },
   1631             "bufferGlobal": {
   1632                 "class": "logging.handlers.MemoryHandler",
   1633                 "capacity": 5,
   1634                 "formatter": "mySimpleFormatter",
   1635                 "target": "fileGlobal",
   1636                 "level": "DEBUG"
   1637                 }
   1638         },
   1639         "loggers": {
   1640             "mymodule": {
   1641                 "level": "DEBUG",
   1642                 "handlers": ["bufferGlobal"],
   1643                 "propagate": "true"
   1644             }
   1645         }
   1646     }
   1647 
   1648     def apply_config(self, conf):
   1649         logging.config.dictConfig(conf)
   1650 
   1651     def test_config0_ok(self):
   1652         # A simple config which overrides the default settings.
   1653         with captured_stdout() as output:
   1654             self.apply_config(self.config0)
   1655             logger = logging.getLogger()
   1656             # Won't output anything
   1657             logger.info(self.next_message())
   1658             # Outputs a message
   1659             logger.error(self.next_message())
   1660             self.assert_log_lines([
   1661                 ('ERROR', '2'),
   1662             ], stream=output)
   1663             # Original logger output is empty.
   1664             self.assert_log_lines([])
   1665 
   1666     def test_config1_ok(self, config=config1):
   1667         # A config defining a sub-parser as well.
   1668         with captured_stdout() as output:
   1669             self.apply_config(config)
   1670             logger = logging.getLogger("compiler.parser")
   1671             # Both will output a message
   1672             logger.info(self.next_message())
   1673             logger.error(self.next_message())
   1674             self.assert_log_lines([
   1675                 ('INFO', '1'),
   1676                 ('ERROR', '2'),
   1677             ], stream=output)
   1678             # Original logger output is empty.
   1679             self.assert_log_lines([])
   1680 
   1681     def test_config2_failure(self):
   1682         # A simple config which overrides the default settings.
   1683         self.assertRaises(StandardError, self.apply_config, self.config2)
   1684 
   1685     def test_config2a_failure(self):
   1686         # A simple config which overrides the default settings.
   1687         self.assertRaises(StandardError, self.apply_config, self.config2a)
   1688 
   1689     def test_config2b_failure(self):
   1690         # A simple config which overrides the default settings.
   1691         self.assertRaises(StandardError, self.apply_config, self.config2b)
   1692 
   1693     def test_config3_failure(self):
   1694         # A simple config which overrides the default settings.
   1695         self.assertRaises(StandardError, self.apply_config, self.config3)
   1696 
   1697     def test_config4_ok(self):
   1698         # A config specifying a custom formatter class.
   1699         with captured_stdout() as output:
   1700             self.apply_config(self.config4)
   1701             #logger = logging.getLogger()
   1702             try:
   1703                 raise RuntimeError()
   1704             except RuntimeError:
   1705                 logging.exception("just testing")
   1706             sys.stdout.seek(0)
   1707             self.assertEqual(output.getvalue(),
   1708                 "ERROR:root:just testing\nGot a [RuntimeError]\n")
   1709             # Original logger output is empty
   1710             self.assert_log_lines([])
   1711 
   1712     def test_config4a_ok(self):
   1713         # A config specifying a custom formatter class.
   1714         with captured_stdout() as output:
   1715             self.apply_config(self.config4a)
   1716             #logger = logging.getLogger()
   1717             try:
   1718                 raise RuntimeError()
   1719             except RuntimeError:
   1720                 logging.exception("just testing")
   1721             sys.stdout.seek(0)
   1722             self.assertEqual(output.getvalue(),
   1723                 "ERROR:root:just testing\nGot a [RuntimeError]\n")
   1724             # Original logger output is empty
   1725             self.assert_log_lines([])
   1726 
   1727     def test_config5_ok(self):
   1728         self.test_config1_ok(config=self.config5)
   1729 
   1730     def test_config6_failure(self):
   1731         self.assertRaises(StandardError, self.apply_config, self.config6)
   1732 
   1733     def test_config7_ok(self):
   1734         with captured_stdout() as output:
   1735             self.apply_config(self.config1)
   1736             logger = logging.getLogger("compiler.parser")
   1737             # Both will output a message
   1738             logger.info(self.next_message())
   1739             logger.error(self.next_message())
   1740             self.assert_log_lines([
   1741                 ('INFO', '1'),
   1742                 ('ERROR', '2'),
   1743             ], stream=output)
   1744             # Original logger output is empty.
   1745             self.assert_log_lines([])
   1746         with captured_stdout() as output:
   1747             self.apply_config(self.config7)
   1748             logger = logging.getLogger("compiler.parser")
   1749             self.assertTrue(logger.disabled)
   1750             logger = logging.getLogger("compiler.lexer")
   1751             # Both will output a message
   1752             logger.info(self.next_message())
   1753             logger.error(self.next_message())
   1754             self.assert_log_lines([
   1755                 ('INFO', '3'),
   1756                 ('ERROR', '4'),
   1757             ], stream=output)
   1758             # Original logger output is empty.
   1759             self.assert_log_lines([])
   1760 
   1761     #Same as test_config_7_ok but don't disable old loggers.
   1762     def test_config_8_ok(self):
   1763         with captured_stdout() as output:
   1764             self.apply_config(self.config1)
   1765             logger = logging.getLogger("compiler.parser")
   1766             # Both will output a message
   1767             logger.info(self.next_message())
   1768             logger.error(self.next_message())
   1769             self.assert_log_lines([
   1770                 ('INFO', '1'),
   1771                 ('ERROR', '2'),
   1772             ], stream=output)
   1773             # Original logger output is empty.
   1774             self.assert_log_lines([])
   1775         with captured_stdout() as output:
   1776             self.apply_config(self.config8)
   1777             logger = logging.getLogger("compiler.parser")
   1778             self.assertFalse(logger.disabled)
   1779             # Both will output a message
   1780             logger.info(self.next_message())
   1781             logger.error(self.next_message())
   1782             logger = logging.getLogger("compiler.lexer")
   1783             # Both will output a message
   1784             logger.info(self.next_message())
   1785             logger.error(self.next_message())
   1786             self.assert_log_lines([
   1787                 ('INFO', '3'),
   1788                 ('ERROR', '4'),
   1789                 ('INFO', '5'),
   1790                 ('ERROR', '6'),
   1791             ], stream=output)
   1792             # Original logger output is empty.
   1793             self.assert_log_lines([])
   1794 
   1795     def test_config_9_ok(self):
   1796         with captured_stdout() as output:
   1797             self.apply_config(self.config9)
   1798             logger = logging.getLogger("compiler.parser")
   1799             #Nothing will be output since both handler and logger are set to WARNING
   1800             logger.info(self.next_message())
   1801             self.assert_log_lines([], stream=output)
   1802             self.apply_config(self.config9a)
   1803             #Nothing will be output since both handler is still set to WARNING
   1804             logger.info(self.next_message())
   1805             self.assert_log_lines([], stream=output)
   1806             self.apply_config(self.config9b)
   1807             #Message should now be output
   1808             logger.info(self.next_message())
   1809             self.assert_log_lines([
   1810                 ('INFO', '3'),
   1811             ], stream=output)
   1812 
   1813     def test_config_10_ok(self):
   1814         with captured_stdout() as output:
   1815             self.apply_config(self.config10)
   1816             logger = logging.getLogger("compiler.parser")
   1817             logger.warning(self.next_message())
   1818             logger = logging.getLogger('compiler')
   1819             #Not output, because filtered
   1820             logger.warning(self.next_message())
   1821             logger = logging.getLogger('compiler.lexer')
   1822             #Not output, because filtered
   1823             logger.warning(self.next_message())
   1824             logger = logging.getLogger("compiler.parser.codegen")
   1825             #Output, as not filtered
   1826             logger.error(self.next_message())
   1827             self.assert_log_lines([
   1828                 ('WARNING', '1'),
   1829                 ('ERROR', '4'),
   1830             ], stream=output)
   1831 
   1832     def test_config11_ok(self):
   1833         self.test_config1_ok(self.config11)
   1834 
   1835     def test_config12_failure(self):
   1836         self.assertRaises(StandardError, self.apply_config, self.config12)
   1837 
   1838     def test_config13_failure(self):
   1839         self.assertRaises(StandardError, self.apply_config, self.config13)
   1840 
   1841     @unittest.skipUnless(threading, 'listen() needs threading to work')
   1842     def setup_via_listener(self, text):
   1843         # Ask for a randomly assigned port (by using port 0)
   1844         t = logging.config.listen(0)
   1845         t.start()
   1846         t.ready.wait()
   1847         # Now get the port allocated
   1848         port = t.port
   1849         t.ready.clear()
   1850         try:
   1851             sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   1852             sock.settimeout(2.0)
   1853             sock.connect(('localhost', port))
   1854 
   1855             slen = struct.pack('>L', len(text))
   1856             s = slen + text
   1857             sentsofar = 0
   1858             left = len(s)
   1859             while left > 0:
   1860                 sent = sock.send(s[sentsofar:])
   1861                 sentsofar += sent
   1862                 left -= sent
   1863             sock.close()
   1864         finally:
   1865             t.ready.wait(2.0)
   1866             logging.config.stopListening()
   1867             t.join(2.0)
   1868 
   1869     def test_listen_config_10_ok(self):
   1870         with captured_stdout() as output:
   1871             self.setup_via_listener(json.dumps(self.config10))
   1872             logger = logging.getLogger("compiler.parser")
   1873             logger.warning(self.next_message())
   1874             logger = logging.getLogger('compiler')
   1875             #Not output, because filtered
   1876             logger.warning(self.next_message())
   1877             logger = logging.getLogger('compiler.lexer')
   1878             #Not output, because filtered
   1879             logger.warning(self.next_message())
   1880             logger = logging.getLogger("compiler.parser.codegen")
   1881             #Output, as not filtered
   1882             logger.error(self.next_message())
   1883             self.assert_log_lines([
   1884                 ('WARNING', '1'),
   1885                 ('ERROR', '4'),
   1886             ], stream=output)
   1887 
   1888     def test_listen_config_1_ok(self):
   1889         with captured_stdout() as output:
   1890             self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
   1891             logger = logging.getLogger("compiler.parser")
   1892             # Both will output a message
   1893             logger.info(self.next_message())
   1894             logger.error(self.next_message())
   1895             self.assert_log_lines([
   1896                 ('INFO', '1'),
   1897                 ('ERROR', '2'),
   1898             ], stream=output)
   1899             # Original logger output is empty.
   1900             self.assert_log_lines([])
   1901 
   1902     def test_out_of_order(self):
   1903         self.apply_config(self.out_of_order)
   1904         handler = logging.getLogger('mymodule').handlers[0]
   1905         self.assertIsInstance(handler.target, logging.Handler)
   1906 
   1907 class ManagerTest(BaseTest):
   1908     def test_manager_loggerclass(self):
   1909         logged = []
   1910 
   1911         class MyLogger(logging.Logger):
   1912             def _log(self, level, msg, args, exc_info=None, extra=None):
   1913                 logged.append(msg)
   1914 
   1915         man = logging.Manager(None)
   1916         self.assertRaises(TypeError, man.setLoggerClass, int)
   1917         man.setLoggerClass(MyLogger)
   1918         logger = man.getLogger('test')
   1919         logger.warning('should appear in logged')
   1920         logging.warning('should not appear in logged')
   1921 
   1922         self.assertEqual(logged, ['should appear in logged'])
   1923 
   1924 
   1925 class ChildLoggerTest(BaseTest):
   1926     def test_child_loggers(self):
   1927         r = logging.getLogger()
   1928         l1 = logging.getLogger('abc')
   1929         l2 = logging.getLogger('def.ghi')
   1930         c1 = r.getChild('xyz')
   1931         c2 = r.getChild('uvw.xyz')
   1932         self.assertTrue(c1 is logging.getLogger('xyz'))
   1933         self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
   1934         c1 = l1.getChild('def')
   1935         c2 = c1.getChild('ghi')
   1936         c3 = l1.getChild('def.ghi')
   1937         self.assertTrue(c1 is logging.getLogger('abc.def'))
   1938         self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
   1939         self.assertTrue(c2 is c3)
   1940 
   1941 
   1942 class HandlerTest(BaseTest):
   1943 
   1944     @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
   1945     @unittest.skipUnless(threading, 'Threading required for this test.')
   1946     def test_race(self):
   1947         # Issue #14632 refers.
   1948         def remove_loop(fname, tries):
   1949             for _ in range(tries):
   1950                 try:
   1951                     os.unlink(fname)
   1952                 except OSError:
   1953                     pass
   1954                 time.sleep(0.004 * random.randint(0, 4))
   1955 
   1956         del_count = 500
   1957         log_count = 500
   1958 
   1959         for delay in (False, True):
   1960             fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
   1961             os.close(fd)
   1962             remover = threading.Thread(target=remove_loop, args=(fn, del_count))
   1963             remover.daemon = True
   1964             remover.start()
   1965             h = logging.handlers.WatchedFileHandler(fn, delay=delay)
   1966             f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
   1967             h.setFormatter(f)
   1968             try:
   1969                 for _ in range(log_count):
   1970                     time.sleep(0.005)
   1971                     r = logging.makeLogRecord({'msg': 'testing' })
   1972                     h.handle(r)
   1973             finally:
   1974                 remover.join()
   1975                 try:
   1976                     h.close()
   1977                 except ValueError:
   1978                     pass
   1979                 if os.path.exists(fn):
   1980                     os.unlink(fn)
   1981 
   1982 
   1983 # Set the locale to the platform-dependent default.  I have no idea
   1984 # why the test does this, but in any case we save the current locale
   1985 # first and restore it at the end.
   1986 @run_with_locale('LC_ALL', '')
   1987 def test_main():
   1988     run_unittest(BuiltinLevelsTest, BasicFilterTest,
   1989                  CustomLevelsAndFiltersTest, MemoryHandlerTest,
   1990                  ConfigFileTest, SocketHandlerTest, MemoryTest,
   1991                  EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
   1992                  ChildLoggerTest, HandlerTest)
   1993 
   1994 if __name__ == "__main__":
   1995     test_main()
   1996