diff --git a/CHANGES.md b/CHANGES.md index e635fbf..8f6f755 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,8 @@ makes it possible to distinguish between a job that explicitly returned `None` and a job that isn't finished yet (see `status` property). +- Remove `logbook` dependency (in favor of `logging`) + - Custom exception handlers can now be configured in addition to, or to fully replace, moving failed jobs to the failed queue. Relevant documentation [here](http://python-rq.org/docs/exceptions/) and diff --git a/requirements.txt b/requirements.txt index 18dfe05..be09e74 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ redis times -logbook argparse diff --git a/rq/scripts/rqworker.py b/rq/scripts/rqworker.py index c870989..0b78d79 100755 --- a/rq/scripts/rqworker.py +++ b/rq/scripts/rqworker.py @@ -1,42 +1,44 @@ #!/usr/bin/env python import sys import argparse -import logbook -from logbook import handlers +import logging +import logging.config + from rq import Queue, Worker from redis.exceptions import ConnectionError from rq.scripts import add_standard_arguments from rq.scripts import setup_redis - -def format_colors(record, handler): - from rq.utils import make_colorizer - if record.level == logbook.WARNING: - colorize = make_colorizer('darkyellow') - elif record.level >= logbook.ERROR: - colorize = make_colorizer('darkred') - else: - colorize = lambda x: x - return '%s: %s' % (record.time.strftime('%H:%M:%S'), colorize(record.msg)) +logger = logging.getLogger(__name__) def setup_loghandlers(args): - if args.verbose: - loglevel = logbook.DEBUG - formatter = None - else: - loglevel = logbook.INFO - formatter = format_colors - - handlers.NullHandler(bubble=False).push_application() - handler = handlers.StreamHandler(sys.stdout, level=loglevel, bubble=False) - if formatter: - handler.formatter = formatter - handler.push_application() - handler = handlers.StderrHandler(level=logbook.WARNING, bubble=False) - if formatter: - handler.formatter = formatter - handler.push_application() + logging.config.dictConfig({ + "version": 1, + "disable_existing_loggers": False, + + "formatters": { + "console": { + "format": "%(asctime)s %(message)s", + "datefmt": "%H:%M:%S", + }, + }, + + "handlers": { + "console": { + "level": "DEBUG", + #"class": "logging.StreamHandler", + "class": "rq.utils.ColorizingStreamHandler", + "formatter": "console", + "exclude": ["%(asctime)s"], + }, + }, + + "root": { + "handlers": ["console"], + "level": "DEBUG" if args.verbose else "INFO" + } + }) def parse_args(): @@ -81,6 +83,7 @@ def main(): setup_loghandlers(args) setup_redis(args) + try: queues = map(Queue, args.queues) w = Worker(queues, name=args.name) diff --git a/rq/utils.py b/rq/utils.py index 2eef8d2..f9c4933 100644 --- a/rq/utils.py +++ b/rq/utils.py @@ -5,6 +5,7 @@ Miscellaneous helper functions. The formatter for ANSI colored console output is heavily based on Pygments terminal colorizing code, originally by Georg Brandl. """ +import logging import os @@ -115,3 +116,34 @@ def make_colorizer(color): def inner(text): return colorizer.colorize(color, text) return inner + + +class ColorizingStreamHandler(logging.StreamHandler): + + levels = { + logging.WARNING: make_colorizer('darkyellow'), + logging.ERROR: make_colorizer('darkred'), + logging.CRITICAL: make_colorizer('darkred'), + } + + def __init__(self, exclude=None, *args, **kwargs): + self.exclude = exclude + super(ColorizingStreamHandler, self).__init__(*args, **kwargs) + + @property + def is_tty(self): + isatty = getattr(self.stream, 'isatty', None) + return isatty and isatty() + + def format(self, record): + message = logging.StreamHandler.format(self, record) + if self.is_tty: + colorize = self.levels.get(record.levelno, lambda x: x) + + # Don't colorize any traceback + parts = message.split('\n', 1) + parts[0] = " ".join([parts[0].split(" ", 1)[0], colorize(parts[0].split(" ", 1)[1])]) + + message = '\n'.join(parts) + + return message diff --git a/rq/worker.py b/rq/worker.py index c936262..4d5c0b4 100644 --- a/rq/worker.py +++ b/rq/worker.py @@ -11,12 +11,8 @@ except ImportError: import socket import signal import traceback +import logging from cPickle import dumps -try: - from logbook import Logger - Logger = Logger # Does nothing except it shuts up pyflakes annoying error -except ImportError: - from logging import Logger from .queue import Queue, get_failed_queue from .connections import get_current_connection from .job import Status @@ -29,6 +25,8 @@ green = make_colorizer('darkgreen') yellow = make_colorizer('darkyellow') blue = make_colorizer('darkblue') +logger = logging.getLogger(__name__) + class StopRequested(Exception): pass @@ -112,7 +110,7 @@ class Worker(object): self._is_horse = False self._horse_pid = 0 self._stopped = False - self.log = Logger('worker') + self.log = logger self.failed_queue = get_failed_queue(connection=self.connection) # By default, push the "move-to-failed-queue" exception handler onto @@ -372,7 +370,7 @@ class Worker(object): signal.signal(signal.SIGTERM, signal.SIG_DFL) self._is_horse = True - self.log = Logger('horse') + self.log = logger success = self.perform_job(job) diff --git a/setup.cfg b/setup.cfg index 8e76f14..73a42b3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ [bdist_rpm] -requires = redis procname logbook +requires = redis procname diff --git a/setup.py b/setup.py index 044bf8d..6154328 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,6 @@ def get_version(): def get_dependencies(): deps = ['redis >= 2.4.0', 'times'] - deps += ['logbook'] # should be soft dependency? if sys.version_info < (2, 7) or \ (sys.version_info >= (3, 0) and sys.version_info < (3, 1)): deps += ['importlib'] diff --git a/tests/__init__.py b/tests/__init__.py index 09c4e4b..398d23a 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,6 +1,7 @@ +import logging import unittest + from redis import Redis -from logbook import NullHandler from rq import push_connection, pop_connection @@ -47,9 +48,8 @@ class RQTestCase(unittest.TestCase): # Store the connection (for sanity checking) cls.testconn = testconn - # Shut up logbook - cls.log_handler = NullHandler() - cls.log_handler.push_thread() + # Shut up logging + logging.disable("ERROR") def setUp(self): # Flush beforewards (we like our hygiene) @@ -66,7 +66,7 @@ class RQTestCase(unittest.TestCase): @classmethod def tearDownClass(cls): - cls.log_handler.pop_thread() + logging.disable(logging.NOTSET) # Pop the connection to Redis testconn = pop_connection()