Permalink
Browse files

Added ability to generate graphs

At the moment any graph can only display 100 data points, a new version
is being worked on that will retrieve chunks from the Cassandra backend
and update the graph as more and more data is available. Only for 0.6 at
the moment too.
  • Loading branch information...
1 parent 436332f commit 589fb98374e5507c4fc99fc848844ca254a1971e @jbohman committed Aug 27, 2010
@@ -20,8 +20,13 @@ def make_map(config):
# CUSTOM ROUTES HERE
map.connect('index', '/', controller='index', action='index')
- map.connect('/log/', controller='log', action='index')
+ map.connect('/log', controller='log', action='index')
+ map.connect('/graph', controller='graph', action='index')
+ map.connect('/graph/view', controller='graph', action='view')
+ map.connect('/graph/ajax', controller='graph', action='ajax')
map.connect('/{controller}/{action}')
map.connect('/{controller}/{action}/{id}')
+ map.redirect('/*(url)/', '/{url}', _redirect_code='301 Moved Permanently')
+
return map
@@ -0,0 +1,34 @@
+import logging
+
+from pylons import request, response, session, tmpl_context as c, url, config
+from pylons.decorators import jsonify
+from pylons.controllers.util import abort, redirect
+
+from logsandra.lib.base import BaseController, render
+from logsandra.model import LogEntry, CassandraClient
+
+log = logging.getLogger(__name__)
+
+class GraphController(BaseController):
+
+ def index(self):
+ return render('/graph_index.html')
+
+ def view(self):
+ c.keyword = request.GET['keyword']
+ return render('/graph_view.html')
+
+ @jsonify
+ def ajax(self):
+ cassandra_client = CassandraClient(config['ident'], config['cassandra_host'], config['cassandra_port'], config['cassandra_timeout'])
+ log_entries = LogEntry(cassandra_client)
+
+ keyword = request.GET['keyword']
+ column_next = ''
+ if 'next' in request.GET and request.GET['next']:
+ column_next = long(request.GET['next'])
+
+ return {'result': log_entries.get_date_count(keyword, column_next=column_next)}
+
+ def error(self):
+ return 'Error, could not parse date'
@@ -1,8 +1,5 @@
import logging
import datetime
-import time
-import pycassa
-import struct
from pylons import request, response, session, tmpl_context as c, url, config
from pylons.controllers.util import abort, redirect
@@ -6,6 +6,7 @@
import pycassa
from cassandra.ttypes import NotFoundException
from ordereddict import OrderedDict
+from collections import defaultdict
__all__ = ['CassandraClient', 'LogEntry']
@@ -51,6 +52,26 @@ def add(self, date, entry, source, keywords):
return True
+ def get_date_count(self, keyword, column_next='', column_finish='', column_count=100):
+ if column_next:
+ column_next = long_struct.pack(column_next)
+
+ try:
+ result = self.client.cf_by_date.get(str(keyword), column_start=column_next, column_count=column_count, column_reversed=True)
+ except NotFoundException:
+ return [], None
+
+ month_dict = defaultdict(int)
+ last = None
+ for key, value in result.items():
+ date = from_long(key)
+ hours = datetime.datetime(date.year, date.month, date.day, date.hour, 0, 0)
+ month_dict[hours] += 1
+ last = key
+
+ return [(str(k), month_dict[k]) for k in sorted(month_dict.keys(), reverse=True)], long_struct.unpack(last)[0] - 1
+
+
def get_by_keyword(self, keyword, column_start='', column_finish='', column_count=30, action_next=None, action_prev=None):
if action_next and action_prev:
raise AttributeError('action_next and action_prev is mutually exclusive')

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -4,10 +4,18 @@
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Logsandra</title>
+ <script type="text/javascript" src="{{ url('/jquery-1.4.2.min.js') }}"></script>
+ {% block head %}{% endblock %}
</head>
<body>
<h1>Logsandra</h1>
+ <div id="navigation">
+ <ul>
+ <li><a href="{{ url(controller='log', action='index') }}">Search</a></li>
+ <li><a href="{{ url(controller='graph', action='index') }}">Graph</a></li>
+ </ul>
+ </div>
<div id="content">
{% block content %}{% endblock %}
</div>
@@ -0,0 +1,24 @@
+{%- extends "base.html" %}
+
+{% block content %}
+
+<form id="create_graph_form" method="get" action="{{ url(controller='graph', action='view') }}">
+
+ <p>Create your own graph</p>
+
+ <p>You can create your own graph by keyword and date interval (optional). You can also set the graph to auto update on a set interval (much like cron).</p>
+
+ Keyword
+ <input type="text" name="keyword" /><br />
+
+ From date (on form: 2010-12-24 20:00:00)
+ <input type="text" name="date_from" /><br />
+
+ To date (on form: 2010-12-24 20:00:00)
+ <input type="text" name="date_to" /><br />
+
+ <input type="submit" name="graph" value="Create graph" />
+
+</form>
+
+{% endblock %}
@@ -0,0 +1,27 @@
+{%- extends "base.html" %}
+
+{% block head %}
+<script type="text/javascript" src="http://www.google.com/jsapi"></script>
+<script type="text/javascript">
+ google.load("visualization", "1", {packages:["corechart"]});
+ google.setOnLoadCallback(drawChart);
+ function drawChart() {
+ $.getJSON('{{ url(controller='graph', action='ajax') }}', {'keyword': "{{ c.keyword }}"}, function(result) {
+ var data = new google.visualization.DataTable();
+ data.addColumn('string', 'Date');
+ data.addColumn('number', 'Count');
+ data.addRows(result.result[0])
+ data.sort({column: 0});
+
+ var chart = new google.visualization.AreaChart(document.getElementById('chart_div'));
+ chart.draw(data, {width: 800, height: 480, title: ''});
+ });
+ }
+</script>
+{% endblock %}
+
+{% block content %}
+
+<div id="chart_div"></div>
+
+{% endblock %}
@@ -2,7 +2,6 @@
{% block content %}
-
<form method="get" action="{{ url(controller='log', action='view') }}">
<p>Filter log by status code (required) and date (optional).</p>
@@ -7,10 +7,14 @@
{% if c.prev_url %}
<a href="{{ c.prev_url }}">Prev</a>
+ {% else %}
+ Prev
{% endif %}
{% if c.next_url %}
<a href="{{ c.next_url }}">Next</a>
+ {% else %}
+ Next
{% endif %}
{% endif %}

0 comments on commit 589fb98

Please sign in to comment.