Permalink
Browse files

Added key inspection.

  • Loading branch information...
1 parent 47dfb9d commit 8b79ebc6f2134d435ed6cb461a17992c3033f5f1 @ionelmc committed Nov 7, 2011
Showing with 139 additions and 32 deletions.
  1. +26 −4 src/redisboard/static/redisboard/admin.css
  2. +46 −3 src/redisboard/templates/redisboard/inspect.html
  3. +67 −25 src/redisboard/views.py
@@ -4,21 +4,43 @@
}
.inspect-form #content-main {
- width: 980px;
+ min-width: 980px;
margin: 0 auto;
float: none;
}
.inspect-form table {
width: 100%;
}
-.inspect-form td:nth-child(n+2) {
+.inspect-form .database-details td:first-child {
+ width: 60%;
+}
+.inspect-form .server-status td {
+ text-align: center;
+}
+.inspect-form .database-details td:nth-child(n+2) {
text-align: center;
}
-.inspect-form td:nth-child(n+2) {
+.inspect-form .key-details td {
text-align: center;
}
-.details td {
+.inspect-form .key-data td {
+ text-align: left;
+}
+.inspect-form .key-details tr:hover td,
+.inspect-form .key-data tr:hover td,
+.inspect-form .database-details tr:hover td {
+ background: #FFFF80;
+}
+.inspect-form .key-data td:nth-child(2) {
+ font-family: monospace;
+ width: 99%;
+}
+.inspect-form .details td {
width: 50%;
+ word-break: break-all;
+}
+.inspect-form .key-data td:nth-child(2) {
+ word-break: break-all;
}
td.error {
font-weight: bold;
@@ -32,8 +32,9 @@
{% endif %}
{% block content %}
+{% spaceless %}
<div id="content-main">
- <table>
+ <table class="server-status">
<tr>
<th>{% trans "Status" %}</th>
<th>{% trans "Memory" %}</th>
@@ -57,7 +58,7 @@
{% for db, db_detail in databases.items %}
- <fieldset class="module aligned ">
+ <fieldset class="module aligned database-details">
<h2>{% trans "Database" %} {{ db }}</h2>
<table>
<tr>
@@ -69,21 +70,23 @@
<th>{% trans "TTL" %}</th>
<th>{% trans "Encoding" %}</th>
<th>{% trans "Length" %}</th>
+ <th>{% trans "Items" %}</th>
<th>{% trans "LRU" %}</th>
<th>{% trans "Refs" %}</th>
<th>{% trans "Other" %}</th>
</tr>
{% for key, key_detail in db_detail.items %}
<tr>
- <td>{{ key }}</td>
+ <td><a href="{% url 'admin:redisboard_redisserver_inspect' original.id %}?key={{ key|escape }}&db={{ db }}">{{ key }}</td>
{% if key_detail.error %}
<td class="error" colspan="7">{% trans "ERROR:" %} {{ key_detail.error }}</td>
{% else %}
<td>{{ key_detail.type }}</td>
<td>{{ key_detail.ttl }}</td>
<td>{{ key_detail.details.encoding }}</td>
<td>{{ key_detail.details.serializedlength }}</td>
+ <td>{{ key_detail.length }}</td>
<td>{{ key_detail.details.lru|default:"n/a" }} (idle: {{ key_detail.details.lru_seconds_idle|default:"n/a" }})</td>
<td>{{ key_detail.details.refcount }}</td>
<td>At: {{ key_detail.details.at }}</td>
@@ -94,5 +97,45 @@
</table>
</fieldset>
{% endfor %}
+ {% if key_details %}
+ <fieldset class="module aligned key-details">
+ <h2>{% trans "Key details" %}: {{ key_details.name }}</h2>
+ <table>
+ <tr>
+ <th>{% trans "Type" %}</th>
+ <th>{% trans "TTL" %}</th>
+ <th>{% trans "Encoding" %}</th>
+ <th>{% trans "Length" %}</th>
+ <th>{% trans "Items" %}</th>
+ <th>{% trans "LRU" %}</th>
+ <th>{% trans "Refs" %}</th>
+ <th>{% trans "Other" %}</th>
+ </tr>
+ <tr>
+ {% if key_details.error %}
+ <td class="error" colspan="7">{% trans "ERROR:" %} {{ key_detail.error }}</td>
+ {% else %}
+ <td>{{ key_details.type }}</td>
+ <td>{{ key_details.ttl }}</td>
+ <td>{{ key_details.details.encoding }}</td>
+ <td>{{ key_details.details.serializedlength }}</td>
+ <td>{{ key_details.length }}</td>
+ <td>{{ key_details.details.lru|default:"n/a" }} (idle: {{ key_details.details.lru_seconds_idle|default:"n/a" }})</td>
+ <td>{{ key_details.details.refcount }}</td>
+ <td>At: {{ key_details.details.at }}</td>
+ {% endif %}
+ </tr>
+ </table>
+ </fieldset>
+ <fieldset class="module aligned key-data">
+ <h2>{% trans "Key data" %}: {{ key_details.name }}</h2>
+ <table>
+ {% for key, value in key_details.data %}
+ <tr><td>{{ key }}</td><td>{{ value }}</td></tr>
+ {% endfor %}
+ </table>
+ </fieldset>
+ {% endif %}
</div>
+{% endspaceless %}
{% endblock %}
View
@@ -3,6 +3,8 @@
from django.shortcuts import render
from django.utils.datastructures import SortedDict
+from django.conf import settings
+
from redis.exceptions import ResponseError
def safeint(value):
@@ -14,44 +16,84 @@ def safeint(value):
def _fixup_pair((a, b)):
return a, safeint(b)
-def _get_key_details(conn, db):
+LENGTH_GETTERS = {
+ 'list': lambda conn, key: conn.llen(key),
+ 'string': lambda conn, key: conn.strlen(key),
+ 'set': lambda conn, key: conn.scard(key),
+ 'zset': lambda conn, key: conn.zcount(key, '-inf', '+inf'),
+ 'hash': lambda conn, key: conn.hlen(key),
+}
+
+def _get_key_info(conn, key):
+ try:
+ details = conn.execute_command('DEBUG', 'OBJECT', key)
+ obj_type = conn.type(key)
+ obj_length = LENGTH_GETTERS[obj_type](conn, key)
+ return {
+ 'type': conn.type(key),
+ 'name': key,
+ 'details': dict(
+ _fixup_pair(i.split(':')) for i in details.split() if ':' in i
+ ),
+ 'length': obj_length,
+ 'ttl': conn.ttl(key),
+ }
+ except ResponseError, e:
+ logger.exception("Failed to get details for key %r", key)
+ return {
+ 'type': "n/a",
+ 'length': "n/a",
+ 'name': key,
+ 'details': {},
+ 'error': e,
+ 'ttl': "n/a",
+ }
+
+VALUE_GETTERS = {
+ 'list': lambda conn, key, start=0, end=-1: enumerate(conn.lrange(key, start, end)),
+ 'string': lambda conn, key, *args: [('string', conn.get(key))],
+ 'set': lambda conn, key, *args: enumerate(conn.smembers(key)),
+ 'zset': lambda conn, key, start=0, end=-1: enumerate(conn.zrange(key, start, end)),
+ 'hash': lambda conn, key, *args: conn.hgetall(key).iteritems(),
+}
+
+def _get_key_details(conn, db, key):
+ conn.execute_command('SELECT', db)
+ details = _get_key_info(conn, key)
+ details['data'] = VALUE_GETTERS[details['type']](conn, key)
+ return details
+
+
+def _get_db_details(conn, db):
conn.execute_command('SELECT', db)
keys = conn.keys()
key_details = {}
for key in keys:
- try:
- details = conn.execute_command('DEBUG', 'OBJECT', key)
- key_details[key] = {
- 'type': conn.type(key),
- 'details': dict(
- _fixup_pair(i.split(':')) for i in details.split() if ':' in i
- ),
- 'ttl': conn.ttl(key),
- }
- except ResponseError, e:
- logger.exception("Failed to get details for key %r", key)
- key_details[key] = {
- 'type': "n/a",
- 'details': {},
- 'error': e,
- 'ttl': "n/a",
- }
+ key_details[key] = _get_key_info(conn, key)
return key_details
def inspect(request, server):
stats = server.stats
+ conn = server.connection
+ database_details = SortedDict()
+ key_details = None
+
if stats['status'] == 'UP':
- conn = server.connection
- databases = sorted(name[2:] for name in conn.info() if name.startswith('db'))
- database_details = SortedDict()
- for db in databases:
- database_details[db] = _get_key_details(conn, db)
- else:
- database_details = {}
+ if 'key' in request.GET:
+ key = request.GET['key']
+ db = request.GET.get('db', 0)
+ key_details = _get_key_details(conn, db, key)
+ else:
+ databases = sorted(name[2:] for name in conn.info() if name.startswith('db'))
+
+ for db in databases:
+ database_details[db] = _get_db_details(conn, db)
+
return render(request, "redisboard/inspect.html", {
'databases': database_details,
+ 'key_details': key_details,
'original': server,
'stats': stats,
'app_label': 'redisboard',

0 comments on commit 8b79ebc

Please sign in to comment.