From ad7b10dec55e72a262a205cd1bbfdf114a8daaea Mon Sep 17 00:00:00 2001 From: WoLpH Date: Mon, 2 Apr 2012 04:28:11 +0200 Subject: [PATCH] added slowlog support --- setup.py | 4 +- src/redisboard/admin.py | 63 +++++++++++++++++++--- src/redisboard/models.py | 28 +++++++++- src/redisboard/static/redisboard/admin.css | 12 +++++ 4 files changed, 97 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index 1ca9478..6e7e907 100644 --- a/setup.py +++ b/setup.py @@ -17,8 +17,8 @@ package_dir={'': 'src'}, include_package_data=True, install_requires=[ - 'redis', - 'Django>=1.3', + 'redis>=2.10.0', + 'Django>=1.4', ], zip_safe=False, classifiers=[ diff --git a/src/redisboard/admin.py b/src/redisboard/admin.py index 3318f1a..27f70ea 100644 --- a/src/redisboard/admin.py +++ b/src/redisboard/admin.py @@ -16,11 +16,35 @@ class Media: } list_display = ( - '__unicode__', 'status', 'memory', 'clients', 'details', 'tools' + '__unicode__', + 'status', + 'memory', + 'clients', + 'details', + 'cpu_utilization', + 'slowlog', + 'tools', ) + list_filter = 'label', 'hostname' ordering = ('hostname', 'port') + def slowlog(self, obj): + output = [(float('inf'), 'Total: %d items' % obj.slowlog_len())] + for log in obj.slowlog_get(): + command = ' '.join(log['command']) + if command[100:]: + command = command[:97] + '...' + + output.append(( + log['duration'], + '%dms: %s' % (log['duration'], command), + )) + + return '
'.join(l for _, l in sorted(output, reverse=True)) + slowlog.allow_tags = True + slowlog.long_description = _('Slowlog') + def status(self, obj): return obj.stats['status'] status.long_description = _("Status") @@ -42,13 +66,38 @@ def tools(self, obj): tools.long_description = _("Tools") def details(self, obj): - return '%s
' % ''.join( - "%s%s" % i for i in - obj.stats['brief_details'].items() - ) + output = [] + for k, v in obj.stats['brief_details'].iteritems(): + output.append('
%s
%s
' % (k, v)) + + return '
%s
' % ''.join(output) details.allow_tags = True details.long_description = _("Details") + def cpu_utilization(self, obj): + data = ( + 'used_cpu_sys', + 'used_cpu_sys_children', + 'used_cpu_user', + 'used_cpu_user_children', + ) + data = dict((k, obj.stats['details'][k]) for k in data) + total_cpu = sum(data.itervalues()) + data['cpu_utilization'] = '%.3f%%' % (total_cpu + / obj.stats['details']['uptime_in_seconds']) + + data = sorted(data.items()) + + output = [] + for k, v in data: + k = k.replace('_', ' ') + output.append('
%s
%s
' % (k, v)) + + return '
%s
' % ''.join(output) + cpu_utilization.allow_tags = True + cpu_utilization.long_description = _('CPU Utilization') + + def get_urls(self): urlpatterns = super(RedisServerAdmin, self).get_urls() try: @@ -69,9 +118,11 @@ def wrapper(*args, **kwargs): def inspect_view(self, request, server_id): server = get_object_or_404(RedisServer, id=server_id) - if self.has_change_permission(request, server) and request.user.has_perm('redisboard.can_inspect'): + if(self.has_change_permission(request, server) + and request.user.has_perm('redisboard.can_inspect')): return inspect(request, server) else: return HttpResponseForbidden("You can't inspect this server.") admin.site.register(RedisServer, RedisServerAdmin) + diff --git a/src/redisboard/models.py b/src/redisboard/models.py index b459620..754b78b 100644 --- a/src/redisboard/models.py +++ b/src/redisboard/models.py @@ -24,6 +24,8 @@ 'uptime_in_seconds', )) +REDISBOARD_SLOWLOG_LEN = getattr(settings, 'REDISBOARD_SLOWLOG_LEN', 10) + def prettify(key, value): if key in REDISBOARD_DETAIL_SECONDS_KEYS: return key, timedelta(seconds=value) @@ -109,8 +111,9 @@ def stats(self): 'clients': info['connected_clients'], 'brief_details': SortedDict( prettify(k, v) - for k, v in sorted(info.items(), key=lambda (k,v): k) - if any(name.match(k) for name in REDISBOARD_DETAIL_FILTERS) + for name in REDISBOARD_DETAIL_FILTERS + for k, v in info.iteritems() + if name.match(k) ) } except redis.exceptions.ConnectionError: @@ -143,3 +146,24 @@ def __unicode__(self): label = label % self.hostname return label + + def slowlog_len(self): + try: + return self.connection.slowlog_len() + except redis.exceptions.ConnectionError: + return 0 + + def slowlog_get(self, limit=REDISBOARD_SLOWLOG_LEN): + try: + slowlog_get = self.connection.slowlog_get(REDISBOARD_SLOWLOG_LEN) + for i, (id, ts, duration, command) in enumerate(slowlog_get): + yield dict( + id=id, + ts=datetime.fromtimestamp(ts), + duration=duration, + command=command, + ) + + except redis.exceptions.ConnectionError: + pass + diff --git a/src/redisboard/static/redisboard/admin.css b/src/redisboard/static/redisboard/admin.css index 638301b..4f6b8f2 100644 --- a/src/redisboard/static/redisboard/admin.css +++ b/src/redisboard/static/redisboard/admin.css @@ -53,3 +53,15 @@ td.error { border: 1px solid gray; margin: 5px; } +dl.details dt{ + margin: 0px; + float: left; + clear: left; + width: 160px; + text-align: right; + font-weight: normal; +} +dl.details dd{ + margin: 0px; + margin-left: 170px; +}