|
|
@@ -1,838 +0,0 @@
|
|
|
-function strip_ending_slash(str) {
|
|
|
- if(str.substr(-1) == '/') {
|
|
|
- return str.substr(0, str.length - 1);
|
|
|
- }
|
|
|
- return str;
|
|
|
-}
|
|
|
-
|
|
|
-function truncate_str(str) {
|
|
|
- if (str.length >= 147) {
|
|
|
- return str.substring(0, 148) + "...";
|
|
|
- }
|
|
|
- return str
|
|
|
-}
|
|
|
-
|
|
|
-function build_graphite_options(options, raw) {
|
|
|
- raw = raw || false;
|
|
|
- var clean_options = [];
|
|
|
- internal_options = ['_t'];
|
|
|
- graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format'];
|
|
|
- graphite_png_options = ['areaMode', 'width', 'height', 'template', 'margin', 'bgcolor',
|
|
|
- 'fgcolor', 'fontName', 'fontSize', 'fontBold', 'fontItalic',
|
|
|
- 'yMin', 'yMax', 'colorList', 'title', 'vtitle', 'lineMode',
|
|
|
- 'lineWith', 'hideLegend', 'hideAxes', 'hideGrid', 'minXstep',
|
|
|
- 'majorGridlineColor', 'minorGridLineColor', 'minorY',
|
|
|
- 'thickness', 'min', 'max', 'tz'];
|
|
|
-
|
|
|
- if(raw) {
|
|
|
- options['format'] = 'json';
|
|
|
- } else {
|
|
|
- // use random parameter to force image refresh
|
|
|
- options["_t"] = options["_t"] || Math.random();
|
|
|
- }
|
|
|
-
|
|
|
- $.each(options, function (key, value) {
|
|
|
- if(raw) {
|
|
|
- if ($.inArray(key, graphite_options) == -1) {
|
|
|
- return;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if ($.inArray(key, graphite_options) == -1 && $.inArray(key, graphite_png_options) == -1) {
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- if (key === "targets") {
|
|
|
- $.each(value, function (index, value) {
|
|
|
- if (raw) {
|
|
|
- // it's normally pointless to use alias() in raw mode, because we apply an alias (name) ourself
|
|
|
- // in the client rendering step. we just need graphite to return the target.
|
|
|
- // but graphite sometimes alters the name of the target in the returned data
|
|
|
- // (https://github.com/graphite-project/graphite-web/issues/248)
|
|
|
- // so we need a good string identifier and set it using alias() (which graphite will honor)
|
|
|
- // so that we recognize the returned output. simplest is just to include the target spec again
|
|
|
- // though this duplicates a lot of info in the url.
|
|
|
- clean_options.push("target=alias(" + encodeURIComponent(value.target) + ",'" + value.target +"')");
|
|
|
- } else {
|
|
|
- clean_options.push("target=alias(color(" +encodeURIComponent(value.target + ",'" + value.color) +"'),'" + value.name +"')");
|
|
|
- }
|
|
|
- });
|
|
|
- } else if (value !== null) {
|
|
|
- clean_options.push(key + "=" + encodeURIComponent(value));
|
|
|
- }
|
|
|
- });
|
|
|
- return clean_options;
|
|
|
-}
|
|
|
-
|
|
|
-// build url for an image. but GET url's are limited in length, so if you have many/long params, some may be missing.
|
|
|
-// could be made smarter for example to favor non-target options because we usually don't want to loose any of those.
|
|
|
-function build_graphite_url(options) {
|
|
|
- var limit = 2000; // http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
|
|
|
- var url = options.graphite_url + "?";
|
|
|
-
|
|
|
- options = build_graphite_options(options, false);
|
|
|
- $.map(options, function(option) {
|
|
|
- if (url.length + option.length < limit) {
|
|
|
- url += '&' + option;
|
|
|
- }
|
|
|
- });
|
|
|
- return url.replace(/\?&/, "?");
|
|
|
-}
|
|
|
-
|
|
|
-function build_anthracite_url(options) {
|
|
|
- url = strip_ending_slash(options.anthracite_url) + '/events/json';
|
|
|
- if ('events_query' in options) {
|
|
|
- url += '?q=' + options['events_query'];
|
|
|
- }
|
|
|
- return url;
|
|
|
-}
|
|
|
-
|
|
|
-function find_definition (target_graphite, options) {
|
|
|
- var matching_i = undefined;
|
|
|
- for (var cfg_i = 0; cfg_i < options.targets.length && matching_i == undefined; cfg_i++) {
|
|
|
- // string match (no globbing)
|
|
|
- if(options.targets[cfg_i].target == target_graphite.target) {
|
|
|
- matching_i = cfg_i;
|
|
|
- }
|
|
|
- // glob match?
|
|
|
- else if(target_graphite.target.graphiteGlob(options.targets[cfg_i].target)) {
|
|
|
- matching_i = cfg_i;
|
|
|
- }
|
|
|
- }
|
|
|
- if (matching_i == undefined) {
|
|
|
- console.error ("internal error: could not figure out which target_option target_graphite '" +
|
|
|
- target_graphite.target + "' comes from");
|
|
|
- return [];
|
|
|
- }
|
|
|
- return options.targets[matching_i];
|
|
|
-}
|
|
|
-
|
|
|
-(function ($) {
|
|
|
- /*
|
|
|
- from graphite-web-0.9.9/graphTemplates.conf.example:
|
|
|
-
|
|
|
- [default]
|
|
|
- background = black
|
|
|
- foreground = white
|
|
|
- majorLine = white
|
|
|
- minorLine = grey
|
|
|
- lineColors = blue,green,red,purple,brown,yellow,aqua,grey,magenta,pink,gold,rose
|
|
|
- fontName = Sans
|
|
|
- fontSize = 10
|
|
|
- fontBold = False
|
|
|
- fontItalic = False
|
|
|
-
|
|
|
- definitions below are from http://graphite.readthedocs.org/en/1.0/url-api.html
|
|
|
- */
|
|
|
- var default_graphite_options = {
|
|
|
- 'bgcolor': '#000000', // background color of the graph
|
|
|
- 'fgcolor' : '#ffffff', // title, legend text, and axis labels
|
|
|
- 'majorLine': '#ffffff',
|
|
|
- 'minorLine': '#afafaf'
|
|
|
- }
|
|
|
- var default_tswidget_options = {
|
|
|
- 'events_color': '#ccff66',
|
|
|
- 'es_events_color': '#ff0066',
|
|
|
- 'events_text_color': '#5C991F'
|
|
|
- }
|
|
|
-
|
|
|
- $.fn.graphite = function (options) {
|
|
|
- if (options === "update") {
|
|
|
- $.fn.graphite.update(this, arguments[1]);
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- // Initialize plugin //
|
|
|
- options = options || {};
|
|
|
- var settings = $.extend({}, $.fn.graphite.defaults, options);
|
|
|
-
|
|
|
- return this.each(function () {
|
|
|
- $this = $(this);
|
|
|
-
|
|
|
- $this.data("graphOptions", settings);
|
|
|
- $.fn.graphite.render($this, settings);
|
|
|
- });
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
- $.fn.graphite.render = function($img, options) {
|
|
|
- $img.attr("src", build_graphite_url(options));
|
|
|
- $img.attr("height", options.height);
|
|
|
- $img.attr("width", options.width);
|
|
|
- };
|
|
|
-
|
|
|
- $.fn.graphite.update = function($img, options) {
|
|
|
- options = options || {};
|
|
|
- $img.each(function () {
|
|
|
- $this = $(this);
|
|
|
- var settings = $.extend({}, $this.data("graphOptions"), options);
|
|
|
- $this.data("graphOptions", settings);
|
|
|
- $.fn.graphite.render($this, settings);
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- // note: graphite json output is a list of dicts like:
|
|
|
- // {"datapoints": [...], "target": "<metricname>" }
|
|
|
- // if you did alias(series, "foo") then "target" will contain the alias
|
|
|
- // (loosing the metricname which is bad, esp. when you had a glob with an alias, then you don't know what's what)
|
|
|
- // rickshaw: options.series is a list of dicts like:
|
|
|
- // { name: "alias", color: "foo", data: [{x: (...), y: (...)} , ...]}
|
|
|
- // we basically tell users to use this dict, with extra 'target' to specify graphite target string
|
|
|
- // flot: d = [[<ts>, <val>], (...)]
|
|
|
- // plot ($(..), [d], ..)
|
|
|
- $.fn.graphiteRick = function (options, on_error) {
|
|
|
- options = options || {};
|
|
|
- var settings = $.extend({}, default_graphite_options, default_tswidget_options, $.fn.graphite.defaults, options);
|
|
|
-
|
|
|
- return this.each(function () {
|
|
|
- $this = $(this);
|
|
|
- $this.data("graphOptions", settings);
|
|
|
- $.fn.graphiteRick.render(this, settings, on_error);
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- $.fn.graphiteFlot = function (options, on_error) {
|
|
|
- if ('zoneFileBasePath' in options) {
|
|
|
- timezoneJS.timezone.zoneFileBasePath = options['zoneFileBasePath'];
|
|
|
- timezoneJS.timezone.init();
|
|
|
- }
|
|
|
- options = options || {};
|
|
|
- var settings = $.extend({}, default_graphite_options, default_tswidget_options, $.fn.graphite.defaults, options);
|
|
|
-
|
|
|
- return this.each(function () {
|
|
|
- $this = $(this);
|
|
|
- $this.data("graphOptions", settings);
|
|
|
- $.fn.graphiteFlot.render(this, settings, on_error);
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- $.fn.graphiteHighcharts = function (options, on_error) {
|
|
|
- if ('zoneFileBasePath' in options) {
|
|
|
- timezoneJS.timezone.zoneFileBasePath = options['zoneFileBasePath'];
|
|
|
- timezoneJS.timezone.init();
|
|
|
- }
|
|
|
- options = options || {};
|
|
|
- var settings = $.extend({}, default_graphite_options, default_tswidget_options, $.fn.graphite.defaults, options);
|
|
|
-
|
|
|
- return this.each(function () {
|
|
|
- $this = $(this);
|
|
|
- $this.data("graphOptions", settings);
|
|
|
- $.fn.graphiteHighcharts.render(this, settings, on_error);
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- $.fn.graphiteFlot.render = function(div, options, on_error) {
|
|
|
- var id = div.getAttribute('id');
|
|
|
- $div = $(div);
|
|
|
- $div.height(options.height);
|
|
|
- $div.width(options.width);
|
|
|
- var events = [];
|
|
|
- var es_events = [];
|
|
|
- var all_targets = [];
|
|
|
- var add_targets = function(response_data) {
|
|
|
- for (var res_i = 0; res_i < response_data.length; res_i++) {
|
|
|
- var target = find_definition(response_data[res_i], options);
|
|
|
- target.label = target.name; // flot wants 'label'
|
|
|
- target.data = [];
|
|
|
- var nulls = 0;
|
|
|
- var non_nulls = 0;
|
|
|
- for (var i in response_data[res_i].datapoints) {
|
|
|
- if(response_data[res_i].datapoints[i][0] == null) {
|
|
|
- nulls++;
|
|
|
- if('drawNullAsZero' in options && options['drawNullAsZero']) {
|
|
|
- response_data[res_i].datapoints[i][0] = 0;
|
|
|
- } else {
|
|
|
- // don't tell flot about null values, it prevents adjacent non-null values from
|
|
|
- // being rendered correctly
|
|
|
- continue;
|
|
|
- }
|
|
|
- } else {
|
|
|
- non_nulls++;
|
|
|
- }
|
|
|
- target.data.push([response_data[res_i].datapoints[i][1] * 1000, response_data[res_i].datapoints[i][0]]);
|
|
|
- }
|
|
|
- if (nulls/non_nulls > 0.3) {
|
|
|
- console.log("warning: rendered target contains " + nulls + " null values, " + non_nulls + " non_nulls");
|
|
|
- }
|
|
|
- all_targets.push(target);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var drawFlot = function(es_data, anthracite_data) {
|
|
|
-
|
|
|
- // default config state modifiers (you can override them in your config objects)
|
|
|
- var states = {
|
|
|
- 'stacked': {
|
|
|
- 'series': {'stack': true, 'lines': {'show': true, 'lineWidth': 0, 'fill': 1}},
|
|
|
- },
|
|
|
- 'lines': {
|
|
|
- // flot lib wants 0 or null. not false o_O
|
|
|
- 'series': {'stack': null, 'lines': { 'show': true, 'lineWidth': 0.6, 'fill': false }}
|
|
|
- }
|
|
|
- };
|
|
|
- if(!('states' in options)) {
|
|
|
- options['states'] = {};
|
|
|
- }
|
|
|
- $.extend(options['states'], states);
|
|
|
-
|
|
|
- function suffixFormatterSI(val, axis) {
|
|
|
- range = axis.max - axis.min;
|
|
|
- lowest = Math.min (range,val);
|
|
|
- if (lowest >= Math.pow(10,12))
|
|
|
- return (val / Math.pow(10,12)).toFixed(axis.tickDecimals) + " T";
|
|
|
- if (lowest >= Math.pow(10,9))
|
|
|
- return (val / Math.pow(10,9)).toFixed(axis.tickDecimals) + " G";
|
|
|
- if (lowest >= Math.pow(10,6))
|
|
|
- return (val / Math.pow(10,6)).toFixed(axis.tickDecimals) + " M";
|
|
|
- if (lowest >= Math.pow(10,3))
|
|
|
- return (val / Math.pow(10,3)).toFixed(axis.tickDecimals) + " k";
|
|
|
- return val.toFixed(axis.tickDecimals);
|
|
|
- }
|
|
|
- function suffixFormatterBinary(val, axis) {
|
|
|
- range = axis.max - axis.min;
|
|
|
- lowest = Math.min (range,val);
|
|
|
- if (lowest >= Math.pow(2,40))
|
|
|
- return (val / Math.pow(2,40)).toFixed(axis.tickDecimals) + " Ti";
|
|
|
- if (lowest >= Math.pow(2,30))
|
|
|
- return (val / Math.pow(2,30)).toFixed(axis.tickDecimals) + " Gi";
|
|
|
- if (lowest >= Math.pow(2,20))
|
|
|
- return (val / Math.pow(2,20)).toFixed(axis.tickDecimals) + " Mi";
|
|
|
- if (lowest >= Math.pow(2,10))
|
|
|
- return (val / Math.pow(2,10)).toFixed(axis.tickDecimals) + " Ki";
|
|
|
- return val.toFixed(axis.tickDecimals);
|
|
|
- }
|
|
|
-
|
|
|
- var buildFlotOptions = function(options) {
|
|
|
- // xaxis color = title color and horizontal lines in grid
|
|
|
- // yaxis color = vtitle color and vertical lines in grid
|
|
|
- // xaxis tickcolor = override vertical lines in grid
|
|
|
- // yaxis tickcolor = override horizontal lines in grid
|
|
|
- // note: flot doesn't distinguish between major and minor line
|
|
|
- // so i use minor, because graphite uses very thin lines which make them look less intense,
|
|
|
- // in flot they seem to be a bit thicker, so generally make them less intense to have them look similar.
|
|
|
- // although they still look more intense than in graphite though.
|
|
|
- // tuning tickLength doesn't seem to help (and no lineWidth for axis..). maybe a graphite bug
|
|
|
- options['tickColor'] = options['minorLine'];
|
|
|
- options['xaxis'] = options['xaxis'] || {};
|
|
|
- $.extend(options['xaxis'], { color: options['fgcolor'], tickColor: options['tickColor'], mode: 'time'});
|
|
|
- if ('tz' in options) {
|
|
|
- options['xaxis']['timezone'] = options['tz'];
|
|
|
- }
|
|
|
- options['yaxis'] = options['yaxis'] || {};
|
|
|
- $.extend(options['yaxis'], { color: options['fgcolor'], tickColor: options['tickColor'], tickFormatter: suffixFormatterSI});
|
|
|
- if('suffixes' in options) {
|
|
|
- if(options['suffixes'] == 'binary') {
|
|
|
- options['yaxis']['tickFormatter'] = suffixFormatterBinary;
|
|
|
- } else if(!options['suffixes']) {
|
|
|
- delete options['yaxis']['tickFormatter'];
|
|
|
- }
|
|
|
- }
|
|
|
- if('title' in options) {
|
|
|
- options['xaxes'] = [{axisLabel: options['title']}];
|
|
|
- }
|
|
|
- if('vtitle' in options) {
|
|
|
- options['yaxes'] = [{position: 'left', axisLabel: options['vtitle']}];
|
|
|
- }
|
|
|
- for (i = 0; i < options['targets'].length; i++ ) {
|
|
|
- options['targets'][i]['color'] = options['targets'][i]['color'];
|
|
|
- }
|
|
|
- if(!('grid' in options)) {
|
|
|
- options['grid'] = {};
|
|
|
- }
|
|
|
- if(options['hover_details']) {
|
|
|
- options['grid']['hoverable'] = true;
|
|
|
- options['grid']['autoHighlight'] = true; // show datapoint being hilighted. true by default but hardcode to make sure
|
|
|
- }
|
|
|
- if(!('markings' in options['grid'])) {
|
|
|
- options['grid']['markings'] = [];
|
|
|
- }
|
|
|
- if(!('selection' in options)) {
|
|
|
- options['selection'] = {
|
|
|
- 'mode': "xy"
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- for (var i = 0; i < events.length; i++) {
|
|
|
- x = events[i].date * 1000;
|
|
|
- options['grid']['markings'].push({ color: options['events_color'], lineWidth: 1, xaxis: { from: x, to: x} });
|
|
|
- }
|
|
|
- // custom es_events loop
|
|
|
- for (var i = 0; i < es_events.length; i++) {
|
|
|
- x = Date.parse(es_events[i]['_source']['@timestamp']);
|
|
|
- options['grid']['markings'].push({ color: options['es_events_color'], lineWidth: 1, xaxis: { from: x, to: x} });
|
|
|
- }
|
|
|
- state = options['state'] || 'lines';
|
|
|
- return $.extend(options, options['states'][state]);
|
|
|
- }
|
|
|
- var plot = $.plot(div, all_targets, buildFlotOptions(options));
|
|
|
- $div.bind('plotselected', function (event, ranges) {
|
|
|
- // clamp the zooming to prevent eternal zoom
|
|
|
-
|
|
|
- if (ranges.xaxis.to - ranges.xaxis.from < 0.00001) {
|
|
|
- ranges.xaxis.to = ranges.xaxis.from + 0.00001;
|
|
|
- }
|
|
|
-
|
|
|
- if (ranges.yaxis.to - ranges.yaxis.from < 0.00001) {
|
|
|
- ranges.yaxis.to = ranges.yaxis.from + 0.00001;
|
|
|
- }
|
|
|
-
|
|
|
- // do the zooming
|
|
|
- zoomed_options = buildFlotOptions(options);
|
|
|
- zoomed_options['xaxis']['min'] = ranges.xaxis.from;
|
|
|
- zoomed_options['xaxis']['max'] = ranges.xaxis.to;
|
|
|
- zoomed_options['yaxis']['min'] = ranges.yaxis.from;
|
|
|
- zoomed_options['yaxis']['max'] = ranges.yaxis.to;
|
|
|
- plot = $.plot(div, all_targets, zoomed_options);
|
|
|
- });
|
|
|
- // add labels
|
|
|
- var o;
|
|
|
- /*for (var i = 0; i < events.length; i++) {
|
|
|
- o = plot.pointOffset({ x: events[i].date * 1000, y: 0});
|
|
|
- msg = '<div style="position:absolute;left:' + (o.left) + 'px;top:' + ( o.top + 35 ) + 'px;';
|
|
|
- msg += 'color:' + options['events_text_color'] + ';font-size:smaller">';
|
|
|
- msg += '<b>' + events[i].type + '</b></br>';
|
|
|
- msg += events[i].desc
|
|
|
- msg += '</div>';
|
|
|
- $div.append(msg);
|
|
|
- }
|
|
|
- for (var i = 0; i < es_events.length; i++) {
|
|
|
- o = plot.pointOffset({ x: Date.parse(es_events[i]['_source']['@timestamp']), y: 0});
|
|
|
- msg = '<div style="background-color:#40FF00;position:absolute;left:' + (o.left) + 'px;top:' + ( o.top + 35 ) + 'px;';
|
|
|
- msg += 'color:' + '#FF0066' + ';font-size:smaller">';
|
|
|
- msg += '<b>tags</b>: ' + es_events[i]['_source']['@tags'].join(' ') + '</br>';
|
|
|
- msg += "<b>env</b>: " + es_events[i]['_source']['@fields']['environment'] + '</br>';
|
|
|
- msg += "<b>msg</b>: " + es_events[i]['_source']['@message'] + '</br>';
|
|
|
- msg += '</div>';
|
|
|
- $div.append(msg);
|
|
|
- }*/
|
|
|
- if (options['line_stack_toggle']) {
|
|
|
- var form = document.getElementById(options['line_stack_toggle']);
|
|
|
- if(options['state'] == 'stacked') {
|
|
|
- lines_checked = '';
|
|
|
- stacked_checked = ' checked';
|
|
|
- } else {
|
|
|
- lines_checked = ' checked';
|
|
|
- stacked_checked = '';
|
|
|
- }
|
|
|
- form.innerHTML= '<input type="radio" name="offset" id="lines" value="lines"'+ lines_checked +'>' +
|
|
|
- '<label class="lines" for="lines">lines</label>' +
|
|
|
- '<br/><input type="radio" name="offset" id="stacked" value="stacked"' + stacked_checked + '>' +
|
|
|
- '<label class="stack" for="stack">stack</label>';
|
|
|
-
|
|
|
- form.addEventListener('change', function(e) {
|
|
|
- var mode = e.target.value;
|
|
|
- options['state'] = mode;
|
|
|
- $.plot(div, all_targets, buildFlotOptions(options));
|
|
|
- }, false);
|
|
|
- }
|
|
|
- function showTooltip(x, y, contents) {
|
|
|
- $("<div id='tooltip_" + id + "'>" + contents + "</div>").css({
|
|
|
- position: "absolute",
|
|
|
- display: "none",
|
|
|
- top: y + 5,
|
|
|
- left: x + 5,
|
|
|
- border: "1px solid #fdd",
|
|
|
- padding: "2px",
|
|
|
- "background-color": "#fee",
|
|
|
- opacity: 0.80
|
|
|
- }).appendTo("body").fadeIn(200);
|
|
|
- }
|
|
|
- var previousPoint = null;
|
|
|
- $(div).bind("plothover", function (event, pos, item) {
|
|
|
- if (item) {
|
|
|
- if (previousPoint != item.dataIndex) {
|
|
|
- previousPoint = item.dataIndex;
|
|
|
- $("#tooltip_" + id).remove();
|
|
|
- var x = item.datapoint[0],
|
|
|
- y = item.datapoint[1].toFixed(2);
|
|
|
- var date = new Date(x);
|
|
|
- showTooltip(item.pageX, item.pageY,
|
|
|
- "Series: " + item.series.label +
|
|
|
- "<br/>Local Time: " + date.toLocaleString() +
|
|
|
- "<br/>UTC Time: " + date.toUTCString() + ")" +
|
|
|
- "<br/>Value: " + y);
|
|
|
- }
|
|
|
- } else {
|
|
|
- $("#tooltip_" + id).remove();
|
|
|
- previousPoint = null;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- }
|
|
|
- data = build_graphite_options(options, true);
|
|
|
- var requests = [];
|
|
|
- requests.push($.ajax({
|
|
|
- accepts: {text: 'application/json'},
|
|
|
- cache: false,
|
|
|
- dataType: 'json',
|
|
|
- url: options['graphite_url'],
|
|
|
- type: "POST",
|
|
|
- data: data.join('&'),
|
|
|
- success: function(data, textStatus, jqXHR ) {
|
|
|
- if(data.length == 0 ) {
|
|
|
- console.warn("no data in graphite response");
|
|
|
- }
|
|
|
- add_targets(data);
|
|
|
- },
|
|
|
- error: function(xhr, textStatus, errorThrown) {
|
|
|
- on_error("Failed to do graphite POST request to " + truncate_str(options['graphite_url']) +
|
|
|
- ": " + textStatus + ": " + errorThrown);
|
|
|
- }
|
|
|
- }));
|
|
|
- if('anthracite_url' in options){
|
|
|
- anthracite_url = build_anthracite_url(options, true);
|
|
|
- requests.push($.ajax({
|
|
|
- accepts: {text: 'application/json'},
|
|
|
- cache: false,
|
|
|
- dataType: 'json',
|
|
|
- url: anthracite_url,
|
|
|
- success: function(data, textStatus, jqXHR ) {
|
|
|
- events = data.events;
|
|
|
- },
|
|
|
- error: function(xhr, textStatus, errorThrown) {
|
|
|
- on_error("Failed to do anthracite GET request to " + truncate_str(anthracite_url) +
|
|
|
- ": " + textStatus + ": " + errorThrown);
|
|
|
- }
|
|
|
- }));
|
|
|
- }
|
|
|
- if('es_url' in options){
|
|
|
- requests.push($.ajax({
|
|
|
- accepts: {text: 'application/json'},
|
|
|
- cache: false,
|
|
|
- dataType: 'json',
|
|
|
- jsonp: 'json',
|
|
|
- url: options['es_url'],
|
|
|
- success: function(data, textStatus, jqXHR ) { es_events = data.hits.hits },
|
|
|
- error: function(xhr, textStatus, errorThrown) {
|
|
|
- on_error("Failed to do elasticsearch request to " + truncate_str(options['es_url']) +
|
|
|
- ": " + textStatus + ": " + errorThrown);
|
|
|
- }
|
|
|
- }));
|
|
|
- }
|
|
|
-
|
|
|
- $.when.apply($, requests).done(drawFlot);
|
|
|
- };
|
|
|
-
|
|
|
- $.fn.graphiteRick.render = function(div, options, on_error) {
|
|
|
- $div = $(div);
|
|
|
- $div.attr("height", options.height);
|
|
|
- $div.attr("width", options.width);
|
|
|
- var drawRick = function(resp_graphite) {
|
|
|
- // note that resp_graphite.length can be != options.targets.length. let's call:
|
|
|
- // * target_graphite a targetstring as returned by graphite
|
|
|
- // * target_option a targetstring configuration
|
|
|
- // if a target_option contains * graphite will return all matches separately unless you use something to aggregate like sumSeries()
|
|
|
- // we must render all target_graphite's, but we must merge in the config from the corresponding target_option.
|
|
|
- // example: for a target_graphite 'stats.foo.bar' we must find a target_option 'stats.foo.bar' *or*
|
|
|
- // anything that causes graphite to match it, such as 'stats.*.bar' (this would be a bit cleaner if graphite's json
|
|
|
- // would include also the originally specified target string)
|
|
|
- // note that this code assumes each target_graphite can only be originating from one target_option,
|
|
|
- // in some unlikely cases this is not correct (there might be overlap between different target_options with globs)
|
|
|
- // but in that case I don't see why taking the settings of any of the possible originating target_options wouldn't be fine.
|
|
|
- var all_targets = [];
|
|
|
- if(resp_graphite.length == 0 ) {
|
|
|
- console.warn("no data in graphite response");
|
|
|
- }
|
|
|
- for (var res_i = 0; res_i < resp_graphite.length; res_i++) {
|
|
|
- var target = find_definition(resp_graphite[res_i], options);
|
|
|
- target.data = [];
|
|
|
- for (var i in resp_graphite[res_i].datapoints) {
|
|
|
- target.data[i] = { x: resp_graphite[res_i].datapoints[i][1], y: resp_graphite[res_i].datapoints[i][0] || 0 };
|
|
|
- }
|
|
|
- all_targets.push(target);
|
|
|
- }
|
|
|
- options['element'] = div;
|
|
|
- options['series'] = all_targets
|
|
|
- for (i = 0; i < options['targets'].length; i++ ) {
|
|
|
- options['targets'][i]['color'] = options['targets'][i]['color'];
|
|
|
- }
|
|
|
- var graph = new Rickshaw.Graph(options);
|
|
|
- if(options['x_axis']) {
|
|
|
- var x_axis = new Rickshaw.Graph.Axis.Time( { graph: graph } );
|
|
|
- }
|
|
|
- if(options['y_axis']) {
|
|
|
- var y_axis = new Rickshaw.Graph.Axis.Y( {
|
|
|
- graph: graph,
|
|
|
- orientation: 'left',
|
|
|
- tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
|
|
|
- element: document.getElementById(options['y_axis']),
|
|
|
- });
|
|
|
- }
|
|
|
- if(options['hover_details']) {
|
|
|
- var hoverDetail = new Rickshaw.Graph.HoverDetail( {
|
|
|
- graph: graph
|
|
|
- } );
|
|
|
- }
|
|
|
- var setRickshawOptions = function (options, graph) {
|
|
|
- if ('state' in options && options['state'] == 'stacked') {
|
|
|
- graph.setRenderer('stack');
|
|
|
- graph.offset = 'zero';
|
|
|
- }
|
|
|
- else { // 'state' is lines
|
|
|
- graph.setRenderer('line');
|
|
|
- graph.offset = 'zero';
|
|
|
- }
|
|
|
- return graph;
|
|
|
- }
|
|
|
- graph = setRickshawOptions(options, graph);
|
|
|
- graph.render();
|
|
|
- if (options['legend']) {
|
|
|
- var legend = new Rickshaw.Graph.Legend({
|
|
|
- graph: graph,
|
|
|
- element: document.getElementById(options['legend'])
|
|
|
- });
|
|
|
- if(options['legend_toggle']) {
|
|
|
- var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
|
|
|
- graph: graph,
|
|
|
- legend: legend
|
|
|
- });
|
|
|
- }
|
|
|
- if(options['legend_reorder']) {
|
|
|
- var order = new Rickshaw.Graph.Behavior.Series.Order({
|
|
|
- graph: graph,
|
|
|
- legend: legend
|
|
|
- });
|
|
|
- }
|
|
|
- if(options['legend_highlight']) {
|
|
|
- var highlighter = new Rickshaw.Graph.Behavior.Series.Highlight({
|
|
|
- graph: graph,
|
|
|
- legend: legend
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- if (options['line_stack_toggle']) {
|
|
|
- var form = document.getElementById(options['line_stack_toggle']);
|
|
|
- if(!options['renderer'] || options['renderer'] == 'area') {
|
|
|
- lines_checked = '';
|
|
|
- stack_checked = ' checked';
|
|
|
- } else {
|
|
|
- lines_checked = ' checked';
|
|
|
- stack_checked = '';
|
|
|
- }
|
|
|
- form.innerHTML= '<input type="radio" name="mode" id="lines" value="lines"'+ lines_checked +'>' +
|
|
|
- '<label class="lines" for="lines">lines</label>' +
|
|
|
- '<br/><input type="radio" name="mode" id="stacked" value="stacked"' + stack_checked + '>' +
|
|
|
- '<label class="stack" for="stacked">stacked</label>';
|
|
|
-
|
|
|
- form.addEventListener('change', function(e) {
|
|
|
- options['state'] = e.target.value;
|
|
|
- graph = setRickshawOptions(options, graph);
|
|
|
- graph.render();
|
|
|
- }, false);
|
|
|
- }
|
|
|
- }
|
|
|
- data = build_graphite_options(options, true);
|
|
|
- $.ajax({
|
|
|
- accepts: {text: 'application/json'},
|
|
|
- cache: false,
|
|
|
- dataType: 'json',
|
|
|
- type: 'POST',
|
|
|
- data: data.join('&'),
|
|
|
- url: options['graphite_url'],
|
|
|
- error: function(xhr, textStatus, errorThrown) {
|
|
|
- on_error("Failed to do graphite POST request to " + truncate_str(options['graphite_url']) +
|
|
|
- ": " + textStatus + ": " + errorThrown);
|
|
|
- }
|
|
|
- }).done(drawRick);
|
|
|
- };
|
|
|
-
|
|
|
- $.fn.graphiteHighcharts.render = function(div, options, on_error) {
|
|
|
- var id = div.getAttribute('id');
|
|
|
- $div = $(div);
|
|
|
- $div.height(options.height);
|
|
|
- $div.width(options.width);
|
|
|
- var drawHighcharts = function(resp_graphite) {
|
|
|
- var hsoptions = {
|
|
|
- chart: {
|
|
|
- renderTo: id,
|
|
|
- type: 'area',
|
|
|
- zoomType: 'xy',
|
|
|
- backgroundColor: options.bgcolor,
|
|
|
- animation: false
|
|
|
- },
|
|
|
- exporting: {
|
|
|
- enabled: false
|
|
|
- },
|
|
|
- credits: {
|
|
|
- enabled: false
|
|
|
- },
|
|
|
- legend: {
|
|
|
- borderWidth: 0,
|
|
|
- useHTML: true,
|
|
|
- itemHoverStyle: {
|
|
|
- color: 'red',
|
|
|
- },
|
|
|
- itemStyle: {
|
|
|
- color: options.fgcolor
|
|
|
- }
|
|
|
- },
|
|
|
- plotOptions: {
|
|
|
- line: {
|
|
|
- lineWidth: 0.8,
|
|
|
- marker: {
|
|
|
- enabled: false
|
|
|
- },
|
|
|
- },
|
|
|
- spline: {
|
|
|
- lineWidth: 0.8,
|
|
|
- marker: {
|
|
|
- enabled: false
|
|
|
- },
|
|
|
- },
|
|
|
- area: {
|
|
|
- stacking: 'normal',
|
|
|
- marker: {
|
|
|
- enabled: false
|
|
|
- },
|
|
|
- lineWidth: 0.8
|
|
|
- },
|
|
|
- areaspline: {
|
|
|
- stacking: 'normal',
|
|
|
- marker: {
|
|
|
- enabled: false
|
|
|
- },
|
|
|
- lineWidth: 0.8
|
|
|
- }
|
|
|
- },
|
|
|
- title: {
|
|
|
- text: options.title,
|
|
|
- style: {
|
|
|
- color: options.fgcolor
|
|
|
- }
|
|
|
- },
|
|
|
- xAxis: {
|
|
|
- type: 'datetime',
|
|
|
- tickPixelInterval: 50,
|
|
|
- labels: {
|
|
|
- rotation: -45,
|
|
|
- align: 'right'
|
|
|
- },
|
|
|
- lineColor: '#777',
|
|
|
- tickColor: '#777',
|
|
|
- maxPadding: 0.01,
|
|
|
- minPadding: 0.01,
|
|
|
- gridLineWidth: 0.2
|
|
|
- },
|
|
|
- yAxis: {
|
|
|
- gridLineColor: 'rgba(255, 255, 255, .3)',
|
|
|
- minorGridLineColor: 'rgba(255,255,255,0.1)',
|
|
|
- title: {
|
|
|
- text: options.vtitle,
|
|
|
- useHTML: true
|
|
|
- },
|
|
|
- maxPadding: 0.01,
|
|
|
- minPadding: 0.01
|
|
|
- },
|
|
|
- tooltip: {
|
|
|
- enabled: options.hover_details,
|
|
|
- crosshairs:[{width:1, color:'#ccc'},{width:1, color:'#ccc'}],
|
|
|
- borderWidth: 0,
|
|
|
- backgroundColor: {
|
|
|
- linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
|
|
|
- stops: [
|
|
|
- [0, 'rgba(96, 96, 96, .8)'],
|
|
|
- [1, 'rgba(16, 16, 16, .8)']
|
|
|
- ]
|
|
|
- },
|
|
|
- style: {
|
|
|
- color: '#FFF'
|
|
|
- },
|
|
|
- useHTML: true,
|
|
|
- formatter: function() {
|
|
|
- return "Series: " + this.series.name +
|
|
|
- "<br/>Local Time: " + Highcharts.dateFormat('%A %B %e %Y %H:%M', this.x) +
|
|
|
- "<br/>Value: " + Highcharts.numberFormat(this.y, 2);
|
|
|
- }
|
|
|
- },
|
|
|
- series: []
|
|
|
- };
|
|
|
- for (var res_i = 0; res_i < resp_graphite.length; res_i++) {
|
|
|
- var target = find_definition(resp_graphite[res_i], options);
|
|
|
- var hstarget = {
|
|
|
- data: [],
|
|
|
- events: {
|
|
|
- click: function() {
|
|
|
- var q;
|
|
|
- if($.isArray(this.graphite_metric)) {
|
|
|
- q = '^' + this.options.graphite_metric.join('$|^') + '$';
|
|
|
- } else {
|
|
|
- q = '^' + this.options.graphite_metric + '$';
|
|
|
- }
|
|
|
- window.location = "/inspect/" + q;
|
|
|
- }
|
|
|
- },
|
|
|
- type: "line",
|
|
|
- animation: false
|
|
|
- };
|
|
|
- if (options.legend && options.legend.labelFormatter) {
|
|
|
- hstarget.name = options.legend.labelFormatter(target.name);
|
|
|
- }
|
|
|
- hstarget.graphite_metric = target.graphite_metric;
|
|
|
- if (options["series"] && options["series"].stack) {
|
|
|
- hstarget.type = "area";
|
|
|
- }
|
|
|
- for (var i in resp_graphite[res_i].datapoints) {
|
|
|
- hstarget.data.push([
|
|
|
- resp_graphite[res_i].datapoints[i][1] * 1000,
|
|
|
- resp_graphite[res_i].datapoints[i][0]
|
|
|
- ]);
|
|
|
- }
|
|
|
- hsoptions.series.push(hstarget)
|
|
|
- }
|
|
|
-
|
|
|
- var hschart = new Highcharts.Chart(hsoptions);
|
|
|
- if (options['line_stack_toggle'])
|
|
|
- {
|
|
|
- var form = document.getElementById(options['line_stack_toggle']);
|
|
|
- var optionshtml = '';
|
|
|
-
|
|
|
- if (options["series"] && options["series"].stack)
|
|
|
- {
|
|
|
- optionshtml += '<option value="stack">stack</option>';
|
|
|
- optionshtml += '<option value="line">lines</option>';
|
|
|
- } else {
|
|
|
- optionshtml += '<option value="line">lines</option>';
|
|
|
- optionshtml += '<option value="stack">stack</option>';
|
|
|
- }
|
|
|
- form.innerHTML = '<select>' + optionshtml + '</select>';
|
|
|
-
|
|
|
- $("select", form).change(function() {
|
|
|
- for (var i in hsoptions.series) {
|
|
|
- var series = hsoptions.series[i];
|
|
|
- series.stack = i;
|
|
|
- if (this.value == "stack") {
|
|
|
- series.type = "area";
|
|
|
- series.stack = 1;
|
|
|
- } else {
|
|
|
- series.type = this.value;
|
|
|
- }
|
|
|
- }
|
|
|
- hschart = new Highcharts.Chart(hsoptions);
|
|
|
- });
|
|
|
- }
|
|
|
- };
|
|
|
- data = build_graphite_options(options, true);
|
|
|
- $.ajax({
|
|
|
- accepts: {text: 'application/json'},
|
|
|
- cache: false,
|
|
|
- dataType: 'json',
|
|
|
- url: options['graphite_url'],
|
|
|
- type: "POST",
|
|
|
- data: data.join('&'),
|
|
|
- error: function(xhr, textStatus, errorThrown) {
|
|
|
- on_error("Failed to do graphite POST request to " + truncate_str(options['graphite_url']) +
|
|
|
- ": " + textStatus + ": " + errorThrown);
|
|
|
- }
|
|
|
- }).done(drawHighcharts);
|
|
|
- };
|
|
|
- // Default settings.
|
|
|
- // Override with the options argument for per-case setup
|
|
|
- // or set $.fn.graphite.defaults.<value> for global changes
|
|
|
- $.fn.graphite.defaults = {
|
|
|
- from: "-1hour",
|
|
|
- height: "300",
|
|
|
- until: "now",
|
|
|
- graphite_url: "/render/",
|
|
|
- width: "940"
|
|
|
- };
|
|
|
-
|
|
|
-}(jQuery));
|