diff --git a/scrapy/utils/log.py b/scrapy/utils/log.py index ccfeb12e1..b54c24fc4 100644 --- a/scrapy/utils/log.py +++ b/scrapy/utils/log.py @@ -32,6 +32,27 @@ class FailureFormatter(logging.Filter): return True +class TopLevelFormatter(logging.Filter): + """Keep only top level loggers's name (direct children from root) from + records. + + This filter will replace Scrapy loggers' names with 'scrapy'. This mimics + the old Scrapy log behaviour and helps shortening long names. + + Since it can't be set for just one logger (it won't propagate for its + children), it's going to be set in the root handler, with a parametrized + `loggers` list where it should act. + """ + + def __init__(self, loggers=None): + self.loggers = loggers or [] + + def filter(self, record): + if any(record.name.startswith(l + '.') for l in self.loggers): + record.name = record.name.split('.', 1)[0] + return True + + DEFAULT_LOGGING = { 'version': 1, 'disable_existing_loggers': False, @@ -91,6 +112,7 @@ def configure_logging(settings=None): ) handler.setFormatter(formatter) handler.setLevel(settings.get('LOG_LEVEL')) + handler.addFilter(TopLevelFormatter(['scrapy'])) logging.root.addHandler(handler) diff --git a/tests/test_utils_log.py b/tests/test_utils_log.py index 42c4d6ea4..e6b8c77d9 100644 --- a/tests/test_utils_log.py +++ b/tests/test_utils_log.py @@ -8,7 +8,8 @@ import unittest from testfixtures import LogCapture from twisted.python.failure import Failure -from scrapy.utils.log import FailureFormatter, LogCounterHandler, StreamLogger +from scrapy.utils.log import (FailureFormatter, TopLevelFormatter, + LogCounterHandler, StreamLogger) from scrapy.utils.test import get_crawler @@ -49,6 +50,41 @@ class FailureFormatterTest(unittest.TestCase): 'test log msg' + os.linesep + '3') +class TopLevelFormatterTest(unittest.TestCase): + + def setUp(self): + self.handler = LogCapture() + self.handler.addFilter(TopLevelFormatter(['test'])) + + def test_top_level_logger(self): + logger = logging.getLogger('test') + with self.handler as l: + logger.warning('test log msg') + + l.check(('test', 'WARNING', 'test log msg')) + + def test_children_logger(self): + logger = logging.getLogger('test.test1') + with self.handler as l: + logger.warning('test log msg') + + l.check(('test', 'WARNING', 'test log msg')) + + def test_overlapping_name_logger(self): + logger = logging.getLogger('test2') + with self.handler as l: + logger.warning('test log msg') + + l.check(('test2', 'WARNING', 'test log msg')) + + def test_different_name_logger(self): + logger = logging.getLogger('different') + with self.handler as l: + logger.warning('test log msg') + + l.check(('different', 'WARNING', 'test log msg')) + + class LogCounterHandlerTest(unittest.TestCase): def setUp(self):