From 9aba44f4bea76885899ff8f11252f4fb6cde9118 Mon Sep 17 00:00:00 2001 From: Doug Kisabaka Date: Thu, 8 Sep 2011 15:04:30 +0200 Subject: Added support for the monitor command. --- README.rst | 12 ++++++++++++ redis/client.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_monitor.py | 10 ++++++++++ 3 files changed, 71 insertions(+) create mode 100644 tests/test_monitor.py diff --git a/README.rst b/README.rst index 383b731..e604ac2 100644 --- a/README.rst +++ b/README.rst @@ -675,6 +675,18 @@ supported: >>> r.pubsub_numpat() 1204 +Monitor +^^^^^^^ +redis-py includes a `Monitor` object that that streams back every command +processed by the Redis server. Use `listen` on the `Monitor` object to block +until message available. + +.. code-block:: pycon + + >>> r = redis.StrictRedis(...) + >>> with sr.monitor() as m: + >>> for command in m.listen(): + >>> print(command) Lua Scripting ^^^^^^^^^^^^^ diff --git a/redis/client.py b/redis/client.py index 24120d2..f246071 100755 --- a/redis/client.py +++ b/redis/client.py @@ -6,6 +6,7 @@ import warnings import time import threading import time as mod_time +import re import hashlib from redis._compat import (basestring, imap, iteritems, iterkeys, itervalues, izip, long, nativestr, safe_unicode) @@ -764,6 +765,9 @@ class Redis(object): """ return PubSub(self.connection_pool, **kwargs) + def monitor(self): + return Monitor(self.connection_pool) + # COMMAND EXECUTION AND PROTOCOL PARSING def execute_command(self, *args, **options): "Execute a command and return a parsed response" @@ -2922,6 +2926,51 @@ class Redis(object): StrictRedis = Redis +class Monitor(object): + """ + Monitor is useful for handling the MONITOR command to the redis server. + next_command() method returns one command from monitor + listen() method yields commands from monitor. + """ + def __init__(self, connection_pool): + self.connection_pool = connection_pool + self.connection = self.connection_pool.get_connection('MONITOR') + + def __enter__(self): + self.connection.send_command('MONITOR') + # check that monitor returns 'OK', but don't return it to user + response = self.connection.read_response() + if not bool_ok(response): + raise RedisError('MONITOR failed: %s' % response) + return self + + def __exit__(self, *args): + self.connection.disconnect() + self.connection_pool.release(self.connection) + + def next_command(self): + "Parse the response from a monitor command" + response = self.connection.read_response() + if isinstance(response, bytes): + response = self.connection.encoder.decode(response, force=True) + command_time, command_data = response.split(' ', 1) + m = re.match(r'\[(\d+) (.+):(\d+)\] (.*)', command_data) + db_id, client_address, client_port, command = m.groups() + command = re.match(r'"(\w*)"+', command).groups() + return { + 'time': float(command_time), + 'db': db_id, + 'client_address': client_address, + 'client_port': client_port, + 'command': command + } + + def listen(self): + "Listen for commands coming to the server." + while 1: + yield self.next_command() + + class PubSub(object): """ PubSub provides publish, subscribe and listen support to Redis channels. diff --git a/tests/test_monitor.py b/tests/test_monitor.py new file mode 100644 index 0000000..da0c992 --- /dev/null +++ b/tests/test_monitor.py @@ -0,0 +1,10 @@ +from __future__ import unicode_literals + + +class TestPipeline(object): + def test_monitor(self, r): + with r.monitor() as m: + r.ping() + response = m.next_command() + assert set(response.keys()) == {'time', 'db', 'client_address', + 'client_port', 'command'} -- cgit v1.2.1