giraffe-web
Version:
Giraffe dashboard for Graphite wrapped in a webserver
659 lines (617 loc) • 20.6 kB
JavaScript
// Generated by CoffeeScript 1.5.0
var auth, changeDashboard, createGraph, dashboard, dataPoll, default_graphite_url, default_period, description, generateDataURL, generateEventsURL, generateGraphiteTargets, getTargetColor, graphScaffold, graphite_url, graphs, init, metrics, period, refresh, refreshSummary, refreshTimer, scheme, toggleCss, _avg, _formatBase1024KMGTP, _last, _max, _min, _sum;
graphite_url = graphite_url || 'demo';
default_graphite_url = graphite_url;
default_period = 1440;
if (scheme === void 0) {
scheme = 'classic9';
}
period = default_period;
dashboard = dashboards[0];
metrics = dashboard['metrics'];
description = dashboard['description'];
refresh = dashboard['refresh'];
refreshTimer = null;
auth = auth != null ? auth : false;
graphs = [];
dataPoll = function() {
var graph, _i, _len, _results;
_results = [];
for (_i = 0, _len = graphs.length; _i < _len; _i++) {
graph = graphs[_i];
_results.push(graph.refreshGraph(period));
}
return _results;
};
_sum = function(series) {
return _.reduce(series, (function(memo, val) {
return memo + val;
}), 0);
};
_avg = function(series) {
return _sum(series) / series.length;
};
_max = function(series) {
return _.reduce(series, (function(memo, val) {
if (memo === null) {
return val;
}
if (val > memo) {
return val;
}
return memo;
}), null);
};
_min = function(series) {
return _.reduce(series, (function(memo, val) {
if (memo === null) {
return val;
}
if (val < memo) {
return val;
}
return memo;
}), null);
};
_last = function(series) {
return _.reduce(series, (function(memo, val) {
if (val !== null) {
return val;
}
return memo;
}), null);
};
_formatBase1024KMGTP = function(y, formatter) {
var abs_y;
if (formatter == null) {
formatter = d3.format(".2r");
}
abs_y = Math.abs(y);
if (abs_y >= 1125899906842624) {
return formatter(y / 1125899906842624) + "P";
} else if (abs_y >= 1099511627776) {
return formatter(y / 1099511627776) + "T";
} else if (abs_y >= 1073741824) {
return formatter(y / 1073741824) + "G";
} else if (abs_y >= 1048576) {
return formatter(y / 1048576) + "M";
} else if (abs_y >= 1024) {
return formatter(y / 1024) + "K";
} else if (abs_y < 1 && y > 0) {
return formatter(y);
} else if (abs_y === 0) {
return 0;
} else {
return formatter(y);
}
};
refreshSummary = function(graph) {
var summary_func, y_data, _ref;
if (!((_ref = graph.args) != null ? _ref.summary : void 0)) {
return;
}
if (graph.args.summary === "sum") {
summary_func = _sum;
}
if (graph.args.summary === "avg") {
summary_func = _avg;
}
if (graph.args.summary === "min") {
summary_func = _min;
}
if (graph.args.summary === "max") {
summary_func = _max;
}
if (graph.args.summary === "last") {
summary_func = _last;
}
if (typeof graph.args.summary === "function") {
summary_func = graph.args.summary;
}
if (!summary_func) {
console.log("unknown summary function " + graph.args.summary);
}
y_data = _.map(_.flatten(_.pluck(graph.graph.series, 'data')), function(d) {
return d.y;
});
return $("" + graph.args.anchor + " .graph-summary").html(graph.args.summary_formatter(summary_func(y_data)));
};
graphScaffold = function() {
var colspan, context, converter, graph_template, i, metric, offset, _i, _len;
graph_template = "{{#dashboard_description}}\n <div class=\"well\">{{{dashboard_description}}}</div>\n{{/dashboard_description}}\n{{#metrics}}\n {{#start_row}}\n <div class=\"row-fluid\">\n {{/start_row}}\n <div class=\"{{span}}\" id=\"graph-{{graph_id}}\">\n <h2>{{metric_alias}} <span class=\"pull-right graph-summary\"><span></h2>\n <div class=\"chart\"></div>\n <div class=\"timeline\"></div>\n <p>{{metric_description}}</p>\n <div class=\"legend\"></div>\n </div>\n {{#end_row}}\n </div>\n {{/end_row}}\n{{/metrics}}";
$('#graphs').empty();
context = {
metrics: []
};
converter = new Markdown.Converter();
if (description) {
context['dashboard_description'] = converter.makeHtml(description);
}
offset = 0;
for (i = _i = 0, _len = metrics.length; _i < _len; i = ++_i) {
metric = metrics[i];
colspan = metric.colspan != null ? metric.colspan : 1;
context['metrics'].push({
start_row: offset % 3 === 0,
end_row: offset % 3 === 2,
graph_id: i,
span: 'span' + (4 * colspan),
metric_alias: metric.alias,
metric_description: metric.description
});
offset += colspan;
}
return $('#graphs').append(Mustache.render(graph_template, context));
};
init = function() {
var dash, i, metric, refreshInterval, _i, _j, _len, _len1;
$('.dropdown-menu').empty();
for (_i = 0, _len = dashboards.length; _i < _len; _i++) {
dash = dashboards[_i];
$('.dropdown-menu').append("<li><a href=\"#\">" + dash.name + "</a></li>");
}
graphScaffold();
graphs = [];
for (i = _j = 0, _len1 = metrics.length; _j < _len1; i = ++_j) {
metric = metrics[i];
graphs.push(createGraph("#graph-" + i, metric));
}
$('.page-header h1').empty().append(dashboard.name);
refreshInterval = refresh || 10000;
if (refreshTimer) {
clearInterval(refreshTimer);
}
return refreshTimer = setInterval(dataPoll, refreshInterval);
};
getTargetColor = function(targets, target) {
var t, _i, _len;
if (typeof targets !== 'object') {
return;
}
for (_i = 0, _len = targets.length; _i < _len; _i++) {
t = targets[_i];
if (!t.color) {
continue;
}
if (t.target === target || t.alias === target) {
return t.color;
}
}
};
generateGraphiteTargets = function(targets) {
var graphite_targets, target, _i, _len;
if (typeof targets === "string") {
return "&target=" + targets;
}
if (typeof targets === "function") {
return "&target=" + (targets());
}
graphite_targets = "";
for (_i = 0, _len = targets.length; _i < _len; _i++) {
target = targets[_i];
if (typeof target === "string") {
graphite_targets += "&target=" + target;
}
if (typeof target === "function") {
graphite_targets += "&target=" + (target());
}
if (typeof target === "object") {
graphite_targets += "&target=" + ((target != null ? target.target : void 0) || '');
}
}
return graphite_targets;
};
generateDataURL = function(targets, annotator_target, max_data_points) {
var data_targets;
annotator_target = annotator_target ? "&target=" + annotator_target : "";
data_targets = generateGraphiteTargets(targets);
return "" + graphite_url + "/render?from=-" + period + "minutes&" + data_targets + annotator_target + "&maxDataPoints=" + max_data_points + "&format=json&jsonp=?";
};
generateEventsURL = function(event_tags) {
var jsonp, tags;
tags = event_tags === '*' ? '' : "&tags=" + event_tags;
jsonp = window.json_fallback ? '' : "&jsonp=?";
return "" + graphite_url + "/events/get_data?from=-" + period + "minutes" + tags + jsonp;
};
createGraph = function(anchor, metric) {
var graph, graph_provider, unstackable, _ref, _ref1, _ref2;
if (graphite_url === 'demo') {
graph_provider = Rickshaw.Graph.Demo;
} else {
graph_provider = Rickshaw.Graph.JSONP.Graphite;
}
unstackable = (_ref = metric.renderer) === 'line' || _ref === 'scatterplot';
return graph = new graph_provider({
anchor: anchor,
targets: metric.target || metric.targets,
summary: metric.summary,
summary_formatter: metric.summary_formatter || _formatBase1024KMGTP,
scheme: metric.scheme || dashboard.scheme || scheme || 'classic9',
annotator_target: ((_ref1 = metric.annotator) != null ? _ref1.target : void 0) || metric.annotator,
annotator_description: ((_ref2 = metric.annotator) != null ? _ref2.description : void 0) || 'deployment',
events: metric.events,
element: $("" + anchor + " .chart")[0],
width: $("" + anchor + " .chart").width(),
height: metric.height || 300,
min: metric.min || 0,
max: metric.max,
null_as: metric.null_as === void 0 ? null : metric.null_as,
renderer: metric.renderer || 'area',
interpolation: metric.interpolation || 'step-before',
unstack: metric.unstack === void 0 ? unstackable : metric.unstack,
stroke: metric.stroke === false ? false : true,
strokeWidth: metric.stroke_width,
dataURL: generateDataURL(metric.target || metric.targets),
onRefresh: function(transport) {
return refreshSummary(transport);
},
onComplete: function(transport) {
var detail, hover_formatter, shelving, xAxis, yAxis;
graph = transport.graph;
xAxis = new Rickshaw.Graph.Axis.Time({
graph: graph
});
xAxis.render();
yAxis = new Rickshaw.Graph.Axis.Y({
graph: graph,
tickFormat: function(y) {
return _formatBase1024KMGTP(y);
},
ticksTreatment: 'glow'
});
yAxis.render();
hover_formatter = metric.hover_formatter || _formatBase1024KMGTP;
detail = new Rickshaw.Graph.HoverDetail({
graph: graph,
yFormatter: function(y) {
return hover_formatter(y);
}
});
$("" + anchor + " .legend").empty();
this.legend = new Rickshaw.Graph.Legend({
graph: graph,
element: $("" + anchor + " .legend")[0]
});
shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
graph: graph,
legend: this.legend
});
if (metric.annotator || metric.events) {
this.annotator = new GiraffeAnnotate({
graph: graph,
element: $("" + anchor + " .timeline")[0]
});
}
return refreshSummary(this);
}
});
};
Rickshaw.Graph.JSONP.Graphite = Rickshaw.Class.create(Rickshaw.Graph.JSONP, {
request: function() {
return this.refreshGraph(period);
},
refreshGraph: function(period) {
var deferred,
_this = this;
deferred = this.getAjaxData(period);
return deferred.done(function(result) {
var annotations, el, i, result_data, series, _i, _len;
if (result.length <= 0) {
return;
}
result_data = _.filter(result, function(el) {
var _ref;
return el.target !== ((_ref = _this.args.annotator_target) != null ? _ref.replace(/["']/g, '') : void 0);
});
result_data = _this.preProcess(result_data);
if (!_this.graph) {
_this.success(_this.parseGraphiteData(result_data, _this.args.null_as));
}
series = _this.parseGraphiteData(result_data, _this.args.null_as);
if (_this.args.annotator_target) {
annotations = _this.parseGraphiteData(_.filter(result, function(el) {
return el.target === _this.args.annotator_target.replace(/["']/g, '');
}), _this.args.null_as);
}
for (i = _i = 0, _len = series.length; _i < _len; i = ++_i) {
el = series[i];
_this.graph.series[i].data = el.data;
_this.addTotals(i);
}
_this.graph.renderer.unstack = _this.args.unstack;
_this.graph.render();
if (_this.args.events) {
deferred = _this.getEvents(period);
deferred.done(function(result) {
return _this.addEventAnnotations(result);
});
}
_this.addAnnotations(annotations, _this.args.annotator_description);
return _this.args.onRefresh(_this);
});
},
addTotals: function(i) {
var avg, label, max, min, series_data, sum;
label = $(this.legend.lines[i].element).find('span.label').text();
$(this.legend.lines[i].element).find('span.totals').remove();
series_data = _.map(this.legend.lines[i].series.data, function(d) {
return d.y;
});
sum = _formatBase1024KMGTP(_sum(series_data));
max = _formatBase1024KMGTP(_max(series_data));
min = _formatBase1024KMGTP(_min(series_data));
avg = _formatBase1024KMGTP(_avg(series_data));
return $(this.legend.lines[i].element).append("<span class='totals pull-right'> Σ: " + sum + " <i class='icon-caret-down'></i>: " + min + " <i class='icon-caret-up'></i>: " + max + " <i class='icon-sort'></i>: " + avg + "</span>");
},
preProcess: function(result) {
var item, _i, _len;
for (_i = 0, _len = result.length; _i < _len; _i++) {
item = result[_i];
if (item.datapoints.length === 1) {
item.datapoints[0][1] = 0;
if (this.args.unstack) {
item.datapoints.push([0, 1]);
} else {
item.datapoints.push([item.datapoints[0][0], 1]);
}
}
}
return result;
},
parseGraphiteData: function(d, null_as) {
var palette, rev_xy, targets;
if (null_as == null) {
null_as = null;
}
rev_xy = function(datapoints) {
return _.map(datapoints, function(point) {
return {
'x': point[1],
'y': point[0] !== null ? point[0] : null_as
};
});
};
palette = new Rickshaw.Color.Palette({
scheme: this.args.scheme
});
targets = this.args.target || this.args.targets;
d = _.map(d, function(el) {
var color, _ref;
if ((_ref = typeof targets) === "string" || _ref === "function") {
color = palette.color();
} else {
color = getTargetColor(targets, el.target) || palette.color();
}
return {
"color": color,
"name": el.target,
"data": rev_xy(el.datapoints)
};
});
Rickshaw.Series.zeroFill(d);
return d;
},
addEventAnnotations: function(events_json) {
var active_annotation, event, _i, _len, _ref, _ref1;
if (!events_json) {
return;
}
this.annotator || (this.annotator = new GiraffeAnnotate({
graph: this.graph,
element: $("" + this.args.anchor + " .timeline")[0]
}));
this.annotator.data = {};
$(this.annotator.elements.timeline).empty();
active_annotation = $(this.annotator.elements.timeline).parent().find('.annotation_line.active').size() > 0;
if ((_ref = $(this.annotator.elements.timeline).parent()) != null) {
_ref.find('.annotation_line').remove();
}
for (_i = 0, _len = events_json.length; _i < _len; _i++) {
event = events_json[_i];
this.annotator.add(event.when, "" + event.what + " " + (event.data || ''));
}
this.annotator.update();
if (active_annotation) {
return (_ref1 = $(this.annotator.elements.timeline).parent()) != null ? _ref1.find('.annotation_line').addClass('active') : void 0;
}
},
addAnnotations: function(annotations, description) {
var annotation_timestamps, _ref;
if (!annotations) {
return;
}
annotation_timestamps = _((_ref = annotations[0]) != null ? _ref.data : void 0).filter(function(el) {
return el.y !== 0 && el.y !== null;
});
return this.addEventAnnotations(_.map(annotation_timestamps, function(a) {
return {
when: a.x,
what: description
};
}));
},
getEvents: function(period) {
var deferred,
_this = this;
this.period = period;
return deferred = $.ajax({
dataType: 'json',
url: generateEventsURL(this.args.events),
error: function(xhr, textStatus, errorThrown) {
if (textStatus === 'parsererror' && /was not called/.test(errorThrown.message)) {
window.json_fallback = true;
return _this.refreshGraph(period);
} else {
return console.log("error loading eventsURL: " + generateEventsURL(_this.args.events));
}
}
});
},
getAjaxData: function(period) {
var deferred;
this.period = period;
return deferred = $.ajax({
dataType: 'json',
url: generateDataURL(this.args.targets, this.args.annotator_target, this.args.width),
error: this.error.bind(this)
});
}
});
Rickshaw.Graph.Demo = Rickshaw.Class.create(Rickshaw.Graph.JSONP.Graphite, {
success: function(data) {
var i, palette, _i;
palette = new Rickshaw.Color.Palette({
scheme: this.args.scheme
});
this.seriesData = [[], [], [], [], [], [], [], [], []];
this.random = new Rickshaw.Fixtures.RandomData(period / 60 + 10);
for (i = _i = 0; _i <= 60; i = ++_i) {
this.random.addData(this.seriesData);
}
this.graph = new Rickshaw.Graph({
element: this.args.element,
width: this.args.width,
height: this.args.height,
min: this.args.min,
max: this.args.max,
renderer: this.args.renderer,
interpolation: this.args.interpolation,
stroke: this.args.stroke,
strokeWidth: this.args.strokeWidth,
series: [
{
color: palette.color(),
data: this.seriesData[0],
name: 'Moscow'
}, {
color: palette.color(),
data: this.seriesData[1],
name: 'Shanghai'
}, {
color: palette.color(),
data: this.seriesData[2],
name: 'Amsterdam'
}, {
color: palette.color(),
data: this.seriesData[3],
name: 'Paris'
}, {
color: palette.color(),
data: this.seriesData[4],
name: 'Tokyo'
}, {
color: palette.color(),
data: this.seriesData[5],
name: 'London'
}, {
color: palette.color(),
data: this.seriesData[6],
name: 'New York'
}
]
});
this.graph.renderer.unstack = this.args.unstack;
this.graph.render();
return this.onComplete(this);
},
refreshGraph: function(period) {
var i, _i, _ref, _results;
if (!this.graph) {
return this.success();
} else {
this.random.addData(this.seriesData);
this.random.addData(this.seriesData);
_.each(this.seriesData, function(d) {
return d.shift();
});
this.args.onRefresh(this);
this.graph.render();
_results = [];
for (i = _i = 0, _ref = this.graph.series.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
_results.push(this.addTotals(i));
}
return _results;
}
}
});
/*
# Events and interaction
*/
$('.dropdown-menu').on('click', 'a', function() {
changeDashboard($(this).text());
$('.dropdown').removeClass('open');
return false;
});
changeDashboard = function(dash_name) {
dashboard = _.where(dashboards, {
name: dash_name
})[0] || dashboards[0];
graphite_url = dashboard['graphite_url'] || default_graphite_url;
description = dashboard['description'];
metrics = dashboard['metrics'];
refresh = dashboard['refresh'];
period || (period = default_period);
init();
return $.bbq.pushState({
dashboard: dashboard.name
});
};
$('.timepanel').on('click', 'a.range', function() {
var dash, timeFrame, _ref;
if (graphite_url === 'demo') {
changeDashboard(dashboard.name);
}
period = $(this).attr('data-timeframe') || default_period;
dataPoll();
timeFrame = $(this).attr('href').replace(/^#/, '');
dash = (_ref = $.bbq.getState()) != null ? _ref.dashboard : void 0;
$.bbq.pushState({
timeFrame: timeFrame,
dashboard: dash || dashboard.name
});
$(this).parent('.btn-group').find('a').removeClass('active');
$(this).addClass('active');
return false;
});
toggleCss = function(css_selector) {
if ($.rule(css_selector).text().match('display: ?none')) {
return $.rule(css_selector, 'style').remove();
} else {
return $.rule("" + css_selector + " {display:none;}").appendTo('style');
}
};
$('#legend-toggle').on('click', function() {
$(this).toggleClass('active');
$('.legend').toggle();
return false;
});
$('#axis-toggle').on('click', function() {
$(this).toggleClass('active');
toggleCss('.y_grid');
toggleCss('.y_ticks');
toggleCss('.x_tick');
return false;
});
$('#x-label-toggle').on('click', function() {
toggleCss('.rickshaw_graph .detail .x_label');
$(this).toggleClass('active');
return false;
});
$('#x-item-toggle').on('click', function() {
toggleCss('.rickshaw_graph .detail .item.active');
$(this).toggleClass('active');
return false;
});
$(window).bind('hashchange', function(e) {
var dash, timeFrame, _ref, _ref1;
timeFrame = ((_ref = e.getState()) != null ? _ref.timeFrame : void 0) || $(".timepanel a.range[data-timeframe='" + default_period + "']")[0].text || "1d";
dash = (_ref1 = e.getState()) != null ? _ref1.dashboard : void 0;
if (dash !== dashboard.name) {
changeDashboard(dash);
}
return $('.timepanel a.range[href="#' + timeFrame + '"]').click();
});
$(function() {
$(window).trigger('hashchange');
return init();
});