Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Initial commit.
  • Loading branch information
malditogeek committed Sep 4, 2012
0 parents commit 07b692a
Show file tree
Hide file tree
Showing 38 changed files with 3,677 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
*.swp
*.swo
.DS_Store
node_modules
6 changes: 6 additions & 0 deletions .nodemonignore
@@ -0,0 +1,6 @@
# Ignore file for Nodemon: https://github.com/remy/nodemon
# Install with 'npm install -g nodemon' then start your app with 'nodemon app.js'
# From then on, all changes you make to /server will cause your app to automatically restart

/client/*
./README.md
15 changes: 15 additions & 0 deletions LICENSE
@@ -0,0 +1,15 @@
Astor, an alternative to StatsD/Graphite.
Copyright (C) 2012 Mauro Pompilio <hackers.are.rockstars@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
38 changes: 38 additions & 0 deletions README.md
@@ -0,0 +1,38 @@
Astor, an alternative to StatsD/Graphite
========================================

API compatible with StatsD. Replacement for Graphite.

![Astor Dashboard](http://i.imgur.com/xtTtS.png)

This is: astor-dashboard
---------------
Real-time metric visualization. Based on SocketStream (Node) and Backbone.

You'll also need: astor-collector
---------------
Metric collector and REST API. Based on EventMachine, Goliath and LevelDB. [Find it here.](https://github.com/malditogeek/astor-collector)

Pre-requisites
--------------

* Node.js
* ZeroMQ dev

Getting started
---------------

If you already have a working Node environment, should be as easy as:

npm install
node app.js

The dashboard consumes data from localhost by default. If you're going to consume data from a different host, you'll need to update:

* app.js
* client/code/app/entry.coffee

Deployment
----------

TBD
41 changes: 41 additions & 0 deletions app.js
@@ -0,0 +1,41 @@
// My SocketStream app

var http = require('http')
, ss = require('socketstream');

// Define a single-page client
ss.client.define('main', {
view: 'app.jade',
css: ['libs', 'app.styl'],
code: ['libs', 'app'],
tmpl: '*'
});

// Serve this client on the root URL
ss.http.route('/', function(req, res){
res.serveClient('main');
})

// Code Formatters
ss.client.formatters.add(require('ss-coffee'));
ss.client.formatters.add(require('ss-jade'));
ss.client.formatters.add(require('ss-stylus'));

// Use server-side compiled Hogan (Mustache) templates. Others engines available
ss.client.templateEngine.use(require('ss-hogan'));

// Minimize and pack assets if you type: SS_ENV=production node app.js
if (ss.env == 'production') ss.client.packAssets();

// Start web server
var server = http.Server(ss.http.middleware);
server.listen(3000);

// Start SocketStream
ss.start(server);

// Astor 0MQ subscriber
var host = '127.0.0.1';
var port = '8890';
var astor = require('./astor.coffee');
astor.connect(ss, host, port);
12 changes: 12 additions & 0 deletions astor.coffee
@@ -0,0 +1,12 @@
zmq = require 'zmq'

exports.connect = (ss, host, port) ->
sub = zmq.socket 'sub'
sub.subscribe 'metric'
sub.subscribe 'alert'
sub.connect "tcp://#{host}:#{port}"
sub.on 'message', (data) ->
msg = data.toString().split(' ')
channel = msg[0]
event_data = JSON.parse(msg[1])
ss.api.publish.all(channel, event_data)
69 changes: 69 additions & 0 deletions client/code/app/alerts.coffee
@@ -0,0 +1,69 @@
# WebSocket event handler for 'alert'
ss.event.on 'alert', (json) ->
alert = new Alert(json)
App.alertsCollection.add(alert)

window.Alert = Backbone.Model.extend({
initialize: (alert) ->
this.set 'id', alert.timestamp
this.set 'type', alert.type
this.set 'name', alert.key
this.set 'timestamp', alert.timestamp
this.set 'reason', alert.reason
this.set 'severity', alert.severity
})

window.AlertsCollection = Backbone.Collection.extend({
model: Alert
})

window.AlertsView = Backbone.View.extend({

tagName: 'ul'
className: 'alerts_list'

initialize: ->
self = this
this.collection.bind("reset", this.render, this)
this.collection.bind("add", (alert) ->
$(self.el).prepend(new AlertItemView({model: alert}).render().el)
)

render: (eventName) ->
_.each(this.collection.models, (Alert) ->
$(this.el).prepend(new AlertItemView({model: alert}).render().el)
, this)
return this

})

window.AlertItemView = Backbone.View.extend({

tagName:"li"

initialize: ->
this.model.bind("change", this.render, this)
this.model.bind("destroy", this.close, this)

render: ->
console.log this.model.toJSON()
$(this.el).html(this.template(this.model.toJSON()))
return this

})

window.AlertsBadgeView = Backbone.View.extend({

tagName: 'span'
className: 'badge badge-important'

initialize: ->
this.collection.bind("add", this.render, this)

render: ->
#console.log JSON.stringify({count: this.collection.size})
#$(this.el).html(this.template(json))
return this

})

123 changes: 123 additions & 0 deletions client/code/app/archived_metrics.coffee
@@ -0,0 +1,123 @@
exports.toggle = (id) ->
metric = App.archivedChartCollection.get(id)
if metric
App.archivedChartCollection.remove(metric)
else
metric = new Metric(id: id)
App.archivedChartCollection.add(metric)


window.Metric = Backbone.Model.extend({
urlRoot: "http://#{ss.astor_api.host}:#{ss.astor_api.port}/v1/metrics",
formated_data: ->
_.map(this.attributes.data, (a) ->
[Date.parse(a[0]), parseFloat(a[1])]
)
})

window.ArchivedMetricsCollection = Backbone.Collection.extend({
url: "http://#{ss.astor_api.host}:#{ss.astor_api.port}/v1/metrics",
model: Metric,
})

window.ArchivedMetricsView = Backbone.View.extend({

tagName: 'ul',
className:'nav nav-list',

initialize: ->
self = this
this.collection.bind("reset", this.render, this)
this.collection.bind("add", (metric) ->
$(self.el).append(new ArchivedMetricItemView({model:metric}).render().el)
)
,

render: (eventName) ->
_.each(this.collection.models, (metric) ->
$(this.el).append(new ArchivedMetricItemView({model: metric}).render().el)
, this)
return this

})

window.ArchivedMetricItemView = Backbone.View.extend({

tagName:"li",

initialize: ->
this.model.bind("change", this.render, this)
this.model.bind("destroy", this.close, this)
,

render: ->
$(this.el).html(this.template(this.model.toJSON()))
return this
,

events: {
"click": "click_ev"
}

click_ev: ->
#console.log this.model.attributes.metric

})

window.ArchiveView = Backbone.View.extend({
initialize: ->
this.offset = '1days'

this.collection.bind("add", this.fetch_datapoints, this)
this.collection.bind("add", this.update_url, this)

this.collection.bind("remove", this.remove, this)
this.collection.bind("remove", this.update_url, this)

this.collection.bind("change", this.add_series, this)

this.chart = new Highcharts.Chart({
chart: {
renderTo: 'archive_chart',
type: 'spline',
zoomType: 'x'
},
title: {
text: ""
},
xAxis: {
type: 'datetime'
},
yAxis: {
title: {
text: ''
}
},
series: [],
credits: {
enabled: false
}
})

setOffset: (offset) ->
this.offset = offset

remove: (metric) ->
this.chart.get(metric.id).remove()

fetch_datapoints: (metric) ->
metric.fetch({data: {offset: this.offset}})

add_series: (metric) ->
this.chart.addSeries({
id: metric.id,
name: metric.get('key'),
data: metric.formated_data()
})

update_url: ->
ids = this.collection.models.map (m) ->
m.id
App.navigate("archive/#{ids.join('+')}", {trigger: false})

})

0 comments on commit 07b692a

Please sign in to comment.