diff --git a/scrapy/utils/log.py b/scrapy/utils/log.py index 889c66015..27cc7b227 100644 --- a/scrapy/utils/log.py +++ b/scrapy/utils/log.py @@ -72,6 +72,9 @@ def configure_logging(settings=None): if settings: logging.root.setLevel(logging.NOTSET) + if settings.getbool('LOG_STDOUT'): + sys.stdout = StreamLogger(logging.getLogger('stdout')) + # Set up the default log handler filename = settings.get('LOG_FILE') if filename: @@ -102,6 +105,22 @@ def log_scrapy_info(settings): logger.info("Overridden settings: %(settings)r", {'settings': d}) +class StreamLogger(object): + """Fake file-like stream object that redirects writes to a logger instance + + Taken from: + http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ + """ + def __init__(self, logger, log_level=logging.INFO): + self.logger = logger + self.log_level = log_level + self.linebuf = '' + + def write(self, buf): + for line in buf.rstrip().splitlines(): + self.logger.log(self.log_level, line.rstrip()) + + class LogCounterHandler(logging.Handler): """Record log levels count into a crawler stats""" diff --git a/tests/test_utils_log.py b/tests/test_utils_log.py index d98dbb574..42c4d6ea4 100644 --- a/tests/test_utils_log.py +++ b/tests/test_utils_log.py @@ -1,12 +1,14 @@ # -*- coding: utf-8 -*- +from __future__ import print_function import os +import sys import logging import unittest from testfixtures import LogCapture from twisted.python.failure import Failure -from scrapy.utils.log import FailureFormatter, LogCounterHandler +from scrapy.utils.log import FailureFormatter, LogCounterHandler, StreamLogger from scrapy.utils.test import get_crawler @@ -75,3 +77,20 @@ class LogCounterHandlerTest(unittest.TestCase): def test_filtered_out_level(self): self.logger.debug('test log msg') self.assertIsNone(self.crawler.stats.get_value('log_count/INFO')) + + +class StreamLoggerTest(unittest.TestCase): + + def setUp(self): + self.stdout = sys.stdout + logger = logging.getLogger('test') + logger.setLevel(logging.WARNING) + sys.stdout = StreamLogger(logger, logging.ERROR) + + def tearDown(self): + sys.stdout = self.stdout + + def test_redirect(self): + with LogCapture() as l: + print('test log msg') + l.check(('test', 'ERROR', 'test log msg'))