Commit c5902ab8 by Torkel Ödegaard

graphite works with modified histogram panel

parent bbcc1ef9
...@@ -96,14 +96,7 @@ require.config({ ...@@ -96,14 +96,7 @@ require.config({
elasticjs: ['angular', '../vendor/elasticjs/elastic'], elasticjs: ['angular', '../vendor/elasticjs/elastic'],
'ts-widget': [ 'ts-widget': ['jquery', 'jquery.flot', 'jquery.flot.selection', 'jquery.flot.stack', 'jquery.flot.time', 'ts-graphite-helpers']
'jquery',
'jquery.flot',
'jquery.flot.selection',
'jquery.flot.stack',
'jquery.flot.time',
'ts-graphite-helpers'
]
}, },
waitSeconds: 60, waitSeconds: 60,
}); });
...@@ -17,8 +17,21 @@ ...@@ -17,8 +17,21 @@
] ]
}, },
"filter": { "filter": {
"list": {}, "list": {
"ids": [] "0": {
"type": "time",
"field": "@timestamp",
"from": "now-1h",
"to": "now",
"mandate": "must",
"active": true,
"alias": "",
"id": 0
}
},
"ids": [
0
]
} }
}, },
"rows": [ "rows": [
...@@ -30,28 +43,99 @@ ...@@ -30,28 +43,99 @@
"collapsable": false, "collapsable": false,
"panels": [ "panels": [
{ {
"title": "graphite",
"error": false, "error": false,
"span": 4, "span": 12,
"editable": false, "editable": true,
"group": [ "group": [
"default" "default"
], ],
"type": "text", "type": "graphite",
"mode": "markdown", "spyable": true
"content": "hej!", }
"style": {}, ],
"title": "", "notice": true
"status": "Stable"
}, },
{ {
"error": false, "title": "Intro",
"span": 8, "height": "150px",
"editable": true,
"collapse": false,
"collapsable": false,
"panels": [
{
"span": 12,
"editable": true, "editable": true,
"group": [ "group": [
"default" "default"
], ],
"type": "graph", "type": "histogram",
"someprop": "hej from config" "mode": "count",
"time_field": "@timestamp",
"value_field": null,
"x-axis": true,
"y-axis": true,
"scale": 1,
"y_format": "none",
"grid": {
"max": null,
"min": 0
},
"queries": {
"mode": "all",
"ids": [
0
]
},
"annotate": {
"enable": false,
"query": "*",
"size": 20,
"field": "_type",
"sort": [
"_score",
"desc"
]
},
"auto_int": true,
"resolution": 100,
"interval": "30s",
"intervals": [
"auto",
"1s",
"1m",
"5m",
"10m",
"30m",
"1h",
"3h",
"12h",
"1d",
"1w",
"1y"
],
"lines": false,
"fill": 0,
"linewidth": 3,
"points": false,
"pointradius": 5,
"bars": true,
"stack": true,
"spyable": true,
"zoomlinks": true,
"options": true,
"legend": true,
"show_query": true,
"interactive": true,
"legend_counts": true,
"timezone": "browser",
"percentage": false,
"zerofill": true,
"derivative": false,
"tooltip": {
"value_type": "cumulative",
"query_as_alias": true
}
} }
], ],
"notice": true "notice": true
...@@ -60,7 +144,7 @@ ...@@ -60,7 +144,7 @@
"editable": true, "editable": true,
"index": { "index": {
"interval": "none", "interval": "none",
"pattern": "[logstash-]YYYY.MM.DD", "pattern": "applogs-error",
"default": "_all", "default": "_all",
"warm_fields": false "warm_fields": false
}, },
...@@ -97,7 +181,10 @@ ...@@ -97,7 +181,10 @@
"2h", "2h",
"1d" "1d"
], ],
"timefield": "@timestamp" "timefield": "@timestamp",
"enable": true,
"now": true,
"filter_id": 0
} }
], ],
"loader": { "loader": {
......
<div>
<div class="row-fluid">
<div class="span12">
No options here
</div>
</div>
</div>
\ No newline at end of file
<div>
<style>
.input-query-alias {
margin-bottom: 5px !important;
}
</style>
<a class="close" ng-click="render();dismiss();" href="">×</a>
<h6>Query Alias</h6>
<form>
<input class="input-medium input-query-alias" type="text" ng-model="queries.list[id].alias" placeholder='Alias...' />
<div>
<i ng-repeat="color in queries.colors" class="pointer" ng-class="{'icon-circle-blank':queries.list[id].color == color,'icon-circle':queries.list[id].color != color}" style="color:{{color}}" ng-click="queries.list[id].color = color;render();"> </i>
</div>
</form>
</div>
\ No newline at end of file
<div ng-controller='filtering' ng-init="init()">
<style>
.filtering-container {
margin-top: 3px;
}
.filter-panel-filter {
display:inline-block;
vertical-align: top;
width: 220px;
padding: 5px 5px 0px 5px;
border: #555 1px solid;
margin: 5px 5px 5px 0px;
}
.filter-panel-filter ul {
margin-bottom: 3px;
}
.filter-must {
border-top: #7EB26D 3px solid;
}
.filter-mustNot {
border-top: #E24D42 3px solid;
}
.filter-either {
border-top: #EF843C 3px solid;
}
.filter-deselected {
opacity: 0.5;
}
.filter-action {
float:right;
margin-bottom: 0px !important;
margin-left: 3px;
}
.filter-mandate {
text-decoration: underline;
cursor: pointer;
}
.filter-apply {
float:right;
margin-bottom: 5px;
}
</style>
<div class='filtering-container'>
<span ng-show="filterSrv.ids.length == 0">
<h5>No filters available</h5>
</span>
<div ng-repeat="id in filterSrv.ids" class="small filter-panel-filter filter-{{filterSrv.list[id].mandate}}" ng-class="{'filter-deselected': !filterSrv.list[id].active}">
<div>
<strong>{{filterSrv.list[id].type}}</strong>
<span ng-show="!filterSrv.list[id].editing && isEditable(filterSrv.list[id])" class="filter-mandate" ng-click="filterSrv.list[id].editing = true">
{{filterSrv.list[id].mandate}}
</span>
<span ng-show="!isEditable(filterSrv.list[id])">
{{filterSrv.list[id].mandate}}
</span>
<span ng-show="filterSrv.list[id].editing">
<select class="input-small" ng-model="filterSrv.list[id].mandate" ng-options="f for f in ['must','mustNot','either']"></select>
</span>
<i class="filter-action pointer icon-remove" bs-tooltip="'Remove'" ng-click="remove(id)"></i>
<i class="filter-action pointer" ng-class="{'icon-check': filterSrv.list[id].active,'icon-check-empty': !filterSrv.list[id].active}" bs-tooltip="'Toggle'" ng-click="toggle(id)"></i>
<i class="filter-action pointer icon-edit" ng-hide="filterSrv.list[id].editing || !isEditable(filterSrv.list[id])" bs-tooltip="'Edit'" ng-click="filterSrv.list[id].editing = true"></i>
</div>
<div ng-hide="filterSrv.list[id].editing && isEditable(filterSrv.list[id])">
<ul class="unstyled">
<li ng-repeat="(key,value) in filterSrv.list[id] track by $index" ng-show="show_key(key)">
<strong>{{key}}</strong> : {{value}}
</li>
</ul>
</div>
<form ng-show="filterSrv.list[id].editing && isEditable(filterSrv.list[id])">
<ul class="unstyled">
<li ng-repeat="key in _.keys(filterSrv.list[id])" ng-show="show_key(key)">
<strong>{{key}}</strong> : <input type='text' ng-model="filterSrv.list[id][key]">
</li>
</ul>
<div>
<input type="submit" value="Apply" ng-click="filterSrv.list[id].editing=undefined;refresh()" class="filter-apply btn btn-success btn-mini" bs-tooltip="'Save and refresh'"/>
<button ng-click="filterSrv.list[id].editing=undefined" class="filter-apply btn btn-mini" bs-tooltip="'Save without refresh'">Save</button>
</div>
</form>
</div>
<i class="link icon-plus-sign" ng-click="add()" bs-tooltip="'Add a query filter'" data-placement="right"></i>
</div>
</div>
\ No newline at end of file
/*
## filtering
*/
define([
'angular',
'app',
'underscore'
],
function (angular, app, _) {
'use strict';
var module = angular.module('kibana.panels.filtering', []);
app.useModule(module);
module.controller('filtering', function($scope, filterSrv, $rootScope, dashboard) {
$scope.panelMeta = {
status : "Stable",
description : "A controllable list of all filters currently applied to the dashboard. You "+
"almost certainly want one of these on your dashboard somewhere."
};
// Set and populate defaults
var _d = {
};
_.defaults($scope.panel,_d);
$scope.$on('filter', function() {
$scope.row.notice = true;
});
$scope.init = function() {
$scope.filterSrv = filterSrv;
};
$scope.remove = function(id) {
filterSrv.remove(id);
};
// This function should be moved to the service
$scope.toggle = function(id) {
filterSrv.list[id].active = !filterSrv.list[id].active;
dashboard.refresh();
};
$scope.add = function(query) {
query = query || '*';
filterSrv.set({
editing : true,
type : 'querystring',
query : query,
mandate : 'must'
},undefined,true);
};
$scope.refresh = function() {
dashboard.refresh();
};
$scope.render = function() {
$rootScope.$broadcast('render');
};
$scope.show_key = function(key) {
return !_.contains(['type','id','alias','mandate','active','editing'],key);
};
$scope.isEditable = function(filter) {
var uneditable = ['time'];
if(_.contains(uneditable,filter.type)) {
return false;
} else {
return true;
}
};
});
});
\ No newline at end of file
...@@ -5,7 +5,7 @@ define([ ...@@ -5,7 +5,7 @@ define([
'underscore', 'underscore',
'ts-widget' 'ts-widget'
], ],
function ($, angular, app, _, timeseriesWidget) { function ($, angular, app, _) {
'use strict'; 'use strict';
var module = angular.module('kibana.panels.graph', []); var module = angular.module('kibana.panels.graph', []);
...@@ -35,10 +35,9 @@ function ($, angular, app, _, timeseriesWidget) { ...@@ -35,10 +35,9 @@ function ($, angular, app, _, timeseriesWidget) {
.directive('mychart', function () { .directive('mychart', function () {
return { return {
restrict: 'E', restrict: 'E',
link: function (scope, elem, attrs) { link: function (scope, elem) {
var tsData = { var tsData = {
graphite_url: 'http://localhost:3030/data', from: '-30d',
from: '-24hours',
until: 'now', until: 'now',
height: '300', height: '300',
width: '740', width: '740',
...@@ -46,34 +45,30 @@ function ($, angular, app, _, timeseriesWidget) { ...@@ -46,34 +45,30 @@ function ($, angular, app, _, timeseriesWidget) {
{ {
name: 'series 1', name: 'series 1',
color: '#CC6699', color: '#CC6699',
target: 'random1', target: 'randomWalk("random1")',
},
{
name: 'series 2',
color: '#006699',
target: 'randomWalk("random2")',
} }
], ],
title: 'horizontal title', title: 'horizontal title',
vtitle: 'vertical title', vtitle: 'vertical title',
drawNullAsZero: false, drawNullAsZero: false,
legend: { container: '#legend_flot_simple', noColumns: 1 }, state: 'stacked',
hover_details: true,
legend: { container: '#legend_flot_simple', noColumns: 4 },
}; };
$("#chart_flot").graphiteFlot(tsData, function(err) { $("#chart_flot").graphiteFlot(tsData, function(err) {
console.log(err); console.log(err);
}); });
console.log('asd'); console.log('asd');
$(elem).html('NJEEEJ!'); $(elem).html('NJEEEJ!');
/*// If the data changes somehow, update it in the chart
scope.$watch('data', function(v){
if(!chart){
chart = $.plot(elem, v , options);
elem.show();
}else{
chart.setData(v);
chart.setupGrid();
chart.draw();
}
});*/
} }
}; };
}); });
}); });
\ No newline at end of file
<div class="editor-row">
<div class="section">
<h5>Values</h5>
<div class="editor-option">
<label class="small">Chart value</label>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total']"></select>
</div>
<div class="editor-option" ng-show="panel.mode != 'count'">
<label class="small">Value Field <tip>This field must contain a numeric value</tip></label>
<input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-large" ng-model="panel.value_field">
</div>
</div>
<div class="section">
<h5>Transform Series</h5>
<div class="editor-option" ng-show="panel.mode != 'count'">
<label class="small">Scale</label>
<input type="text" class="input-mini" ng-model="panel.scale">
</div>
<div class="editor-option">
<label class="small">Seconds <tip>Normalize intervals to per-second</tip></label><input type="checkbox" ng-model="panel.scaleSeconds" ng-checked="panel.scaleSeconds">
</div>
<div class="editor-option">
<label class="small">Derivative <tip>Plot the change per interval in the series</tip></label><input type="checkbox" ng-model="panel.derivative" ng-checked="panel.derivative" ng-change="set_refresh(true)">
</div>
</div>
</div>
<h5>Time Options</h5>
<div class="editor-row">
<div class="editor-option">
<label class="small">Time Field</label>
<input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.time_field">
</div>
<div class="editor-option">
<label class="small">Time correction</label>
<select ng-model="panel.timezone" class='input-small' ng-options="f for f in ['browser','utc']"></select>
</div>
<div class="editor-option">
<label class="small">Auto-interval</label><input type="checkbox" ng-model="panel.auto_int" ng-checked="panel.auto_int" />
</div>
<div class="editor-option" ng-show='panel.auto_int'>
<label class="small">Resolution <tip>Shoot for this many data points, rounding to sane intervals</tip></label>
<input type="number" class='input-mini' ng-model="panel.resolution" ng-change='set_refresh(true)'/>
</div>
<div class="editor-option" ng-hide='panel.auto_int'>
<label class="small">Interval <tip>Use Elasticsearch date math format (eg 1m, 5m, 1d, 2w, 1y)</tip></label>
<input type="text" class='input-mini' ng-model="panel.interval" ng-change='set_refresh(true)'/>
</div>
</div>
\ No newline at end of file
define([
'jquery'
],
function ($) {
'use strict';
String.prototype.graphiteGlob = function(glob) {
var regex = '^';
for (var i = 0; i < glob.length; i++ ) {
var c = glob.charAt(i);
switch (c) {
case '*':
regex += '[^\.]+';
break;
case '.':
regex += '\\.';
break;
default:
regex += c;
}
}
regex += '$';
return this.match(regex);
}
function build_graphite_options(options, raw) {
raw = raw || false;
var clean_options = [];
//var internal_options = ['_t'];
var graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format'];
var 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=" + encodeURIComponent(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;
}
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 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 add_targets(options, response_data) {
var all_targets = [];
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);
}
return all_targets;
}
return {
build_graphite_options: build_graphite_options,
build_graphite_url: build_graphite_url,
add_targets: add_targets
};
});
\ No newline at end of file
define([
'kbn'
],
function (kbn) {
'use strict';
/**
* manages the interval logic
* @param {[type]} interval_string An interval string in the format '1m', '1y', etc
*/
function Interval(interval_string) {
this.string = interval_string;
var info = kbn.describe_interval(interval_string);
this.type = info.type;
this.ms = info.sec * 1000 * info.count;
// does the length of the interval change based on the current time?
if (this.type === 'y' || this.type === 'M') {
// we will just modify this time object rather that create a new one constantly
this.get = this.get_complex;
this.date = new Date(0);
} else {
this.get = this.get_simple;
}
}
Interval.prototype = {
toString: function () {
return this.string;
},
after: function(current_ms) {
return this.get(current_ms, 1);
},
before: function (current_ms) {
return this.get(current_ms, -1);
},
get_complex: function (current, delta) {
this.date.setTime(current);
switch(this.type) {
case 'M':
this.date.setUTCMonth(this.date.getUTCMonth() + delta);
break;
case 'y':
this.date.setUTCFullYear(this.date.getUTCFullYear() + delta);
break;
}
return this.date.getTime();
},
get_simple: function (current, delta) {
return current + (delta * this.ms);
}
};
return Interval;
});
\ No newline at end of file
<div ng-controller='histogram' ng-init="init()" style="min-height:{{panel.height || row.height}}">
<style>
.histogram-legend {
display:inline-block;
padding-right:5px
}
.histogram-legend-dot {
display:inline-block;
height:10px;
width:10px;
border-radius:5px;
}
.histogram-legend-item {
display:inline-block;
}
.histogram-chart {
position:relative;
}
.histogram-options {
padding: 5px;
margin-right: 15px;
margin-bottom: 0px;
}
.histogram-options label {
margin: 0px 0px 0px 10px !important;
}
.histogram-options span {
white-space: nowrap;
}
/* this is actually should be in bootstrap */
.form-inline .checkbox {
display: inline-block;
}
</style>
<div>
<span ng-show='panel.options'>
<a class="link underline small" ng-show='panel.options' ng-click="options=!options">
<i ng-show="!options" class="icon-caret-right"></i><i ng-show="options" class="icon-caret-down"></i> View
</a> |&nbsp
</span>
<span ng-show='panel.zoomlinks && data'>
<!--<a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a>-->
<a class='small' ng-click='zoom(2)'><i class='icon-zoom-out'></i> Zoom Out</a>
</span>
</div>
<form class="form-inline bordered histogram-options" ng-show="options">
<span>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.bars" ng-checked="panel.bars" ng-change="render()">
Bars
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.lines" ng-checked="panel.lines" ng-change="render()">
Lines
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.stack" ng-checked="panel.stack" ng-change="render()">
Stack
</label>
</div>
</span>
<span ng-show="panel.stack">
<div class="checkbox">
<label style="white-space:nowrap" class="small">
<input type="checkbox" ng-model="panel.percentage" ng-checked="panel.percentage" ng-change="render()">
Percent
</label>
</div>
</span>
<span>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.legend" ng-checked="panel.legend" ng-change="render()">
Legend
</label>
</div>
</span>
<span>
<label class="small">Interval</label> <select ng-change="set_interval(panel.interval);get_data();" class="input-small" ng-model="panel.interval" ng-options="interval_label(time) for time in _.union([panel.interval],panel.intervals)"></select>
</span>
</form>
<center><img ng-show='panel.loading && _.isUndefined(data)' src="img/load_big.gif"></center>
<div histogram-chart class="pointer histogram-chart" params="{{panel}}"></div>
<span ng-show="panel.legend" ng-repeat='series in data' class="histogram-legend">
<i class='icon-circle' ng-style="{color: series.info.color}"></i>
<span class='small histogram-legend-item'>
<span ng-if="panel.show_query">{{series.info.alias || series.info.query}}</span>
<span ng-if="!panel.show_query">{{series.info.alias}}</span>
</span>
</span>
</div>
\ No newline at end of file
<h4>Charted</h4>
<div ng-include src="'app/partials/querySelect.html'"></div>
<div class="editor-row">
<h4>Markers</h4>
<div class="small">
Here you can specify a query to be plotted on your chart as a marker. Hovering over a marker will display the field you specify below. If more documents are found than the limit you set, they will be scored by Elasticsearch and events that best match your query will be displayed.
</div>
<style>
.querySelect .query {
margin-right: 5px;
}
.querySelect .selected {
border: 3px solid;
}
.querySelect .unselected {
border: 0px solid;
}
</style>
<p>
<div class="editor-option">
<label class="small">Enable</label>
<input type="checkbox" ng-change="set_refresh(true)" ng-model="panel.annotate.enable" ng-checked="panel.annotate.enable">
</div>
<div class="editor-option" ng-show="panel.annotate.enable">
<label class="small">Marker Query</label>
<input type="text" ng-change="set_refresh(true)" class="input-large" ng-model="panel.annotate.query"/>
</div>
<div class="editor-option" ng-show="panel.annotate.enable">
<label class="small">Tooltip field</label>
<input type="text" class="input-small" ng-model="panel.annotate.field" bs-typeahead="fields.list"/>
</div>
<div class="editor-option" ng-show="panel.annotate.enable">
<label class="small">Limit <tip>Max markers on the chart</tip></label>
<input type="number" class="input-mini" ng-model="panel.annotate.size" ng-change="set_refresh(true)"/>
</div>
<div class="editor-option" ng-show="panel.annotate.enable">
<label class="small">Sort <tip>Determine the most relevant markers using this field</tip></label>
<input type="text" class="input-small" bs-typeahead="fields.list" ng-model="panel.annotate.sort[0]" ng-change="set_refresh(true)" />
<i ng-click="panel.annotate.sort[1] = _.toggle(panel.annotate.sort[1],'desc','asc');set_refresh(true)" ng-class="{'icon-chevron-up': panel.annotate.sort[1] == 'asc','icon-chevron-down': panel.annotate.sort[1] == 'desc'}"></i>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Chart Options</h5>
<div class="editor-option">
<label class="small">Bars</label><input type="checkbox" ng-model="panel.bars" ng-checked="panel.bars">
</div>
<div class="editor-option">
<label class="small">Lines</label><input type="checkbox" ng-model="panel.lines" ng-checked="panel.lines">
</div>
<div class="editor-option">
<label class="small">Points</label><input type="checkbox" ng-model="panel.points" ng-checked="panel.points">
</div>
<div class="editor-option">
<label class="small">Selectable</label><input type="checkbox" ng-model="panel.interactive" ng-checked="panel.interactive">
</div>
<div class="editor-option">
<label class="small">xAxis</label><input type="checkbox" ng-model="panel['x-axis']" ng-checked="panel['x-axis']"></div>
<div class="editor-option">
<label class="small">yAxis</label><input type="checkbox" ng-model="panel['y-axis']" ng-checked="panel['y-axis']"></div>
<div class="editor-option" ng-show="panel.lines">
<label class="small">Line Fill</label>
<select class="input-mini" ng-model="panel.fill" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"></select>
</div>
<div class="editor-option" ng-show="panel.lines">
<label class="small">Line Width</label>
<select class="input-mini" ng-model="panel.linewidth" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"></select>
</div>
<div class="editor-option" ng-show="panel.points">
<label class="small">Point Radius</label>
<select class="input-mini" ng-model="panel.pointradius" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10]"></select>
</div>
<div class="editor-option">
<label class="small">Y Format <tip>Y-axis formatting</tip></label>
<select class="input-small" ng-model="panel.y_format" ng-options="f for f in ['none','short','bytes']"></select>
</div>
</div>
<div class="section">
<h5>Multiple Series</h5>
<div class="editor-option">
<label class="small">Stack</label><input type="checkbox" ng-model="panel.stack" ng-checked="panel.stack">
</div>
<div class="editor-option" ng-show="panel.stack">
<label style="white-space:nowrap" class="small">Percent <tip>Stack as a percentage of total</tip></label>
<input type="checkbox" ng-model="panel.percentage" ng-checked="panel.percentage">
</div>
<div class="editor-option" ng-show="panel.stack">
<label class="small">Stacked Values <tip>How should the values in stacked charts to be calculated?</tip></label>
<select class="input-small" ng-model="panel.tooltip.value_type" ng-options="f for f in ['cumulative','individual']"></select>
</div>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Header<h5>
<div class="editor-option">
<label class="small">Zoom</label><input type="checkbox" ng-model="panel.zoomlinks" ng-checked="panel.zoomlinks" />
</div>
<div class="editor-option">
<label class="small">View</label><input type="checkbox" ng-model="panel.options" ng-checked="panel.options" />
</div>
</div>
<div class="section">
<h5>Legend<h5>
<div class="editor-option">
<label class="small">Legend</label><input type="checkbox" ng-model="panel.legend" ng-checked="panel.legend">
</div>
<div ng-show="panel.legend" class="editor-option">
<label class="small">Query <tip>If no alias is set, show the query in the legend</tip></label><input type="checkbox" ng-model="panel.show_query" ng-checked="panel.show_query">
</div>
<div ng-show="panel.legend" class="editor-option">
<label class="small">Counts</label><input type="checkbox" ng-model="panel.legend_counts" ng-checked="panel.legend_counts">
</div>
</div>
<div class="section">
<h5>Grid<h5>
<div class="editor-option">
<label class="small">Min / <a href='' ng-click="panel.grid.min = _.toggle(panel.grid.min,null,0)">Auto <i class="icon-star" ng-show="_.isNull(panel.grid.min)"></i></a></label>
<input type="number" class="input-small" ng-model="panel.grid.min"/>
</div>
<div class="editor-option">
<label class="small">Max / <a ref='' ng-click="panel.grid.max = _.toggle(panel.grid.max,null,0)">Auto <i class="icon-star" ng-show="_.isNull(panel.grid.max)"></i></a></label>
<input type="number" class="input-small" ng-model="panel.grid.max"/>
</div>
</div>
</div>
define([
'underscore',
'./interval'
],
function (_, Interval) {
'use strict';
var ts = {};
// map compatable parseInt
function base10Int(val) {
return parseInt(val, 10);
}
// trim the ms off of a time, but return it with empty ms.
function getDatesTime(date) {
return Math.floor(date.getTime() / 1000)*1000;
}
/**
* Certain graphs require 0 entries to be specified for them to render
* properly (like the line graph). So with this we will caluclate all of
* the expected time measurements, and fill the missing ones in with 0
* @param {object} opts An object specifying some/all of the options
*
* OPTIONS:
* @opt {string} interval The interval notion describing the expected spacing between
* each data point.
* @opt {date} start_date (optional) The start point for the time series, setting this and the
* end_date will ensure that the series streches to resemble the entire
* expected result
* @opt {date} end_date (optional) The end point for the time series, see start_date
* @opt {string} fill_style Either "minimal", or "all" describing the strategy used to zero-fill
* the series.
*/
ts.ZeroFilled = function (opts) {
opts = _.defaults(opts, {
interval: '10m',
start_date: null,
end_date: null,
fill_style: 'minimal'
});
// the expected differenece between readings.
this.interval = new Interval(opts.interval);
// will keep all values here, keyed by their time
this._data = {};
this.start_time = opts.start_date && getDatesTime(opts.start_date);
this.end_time = opts.end_date && getDatesTime(opts.end_date);
this.opts = opts;
};
/**
* Add a row
* @param {int} time The time for the value, in
* @param {any} value The value at this time
*/
ts.ZeroFilled.prototype.addValue = function (time, value) {
if (time instanceof Date) {
time = getDatesTime(time);
} else {
time = base10Int(time);
}
if (!isNaN(time)) {
this._data[time] = (_.isUndefined(value) ? 0 : value);
}
this._cached_times = null;
};
/**
* Get an array of the times that have been explicitly set in the series
* @param {array} include (optional) list of timestamps to include in the response
* @return {array} An array of integer times.
*/
ts.ZeroFilled.prototype.getOrderedTimes = function (include) {
var times = _.map(_.keys(this._data), base10Int);
if (_.isArray(include)) {
times = times.concat(include);
}
return _.uniq(times.sort(function (a, b) {
// decending numeric sort
return a - b;
}), true);
};
/**
* return the rows in the format:
* [ [time, value], [time, value], ... ]
*
* Heavy lifting is done by _get(Min|Default|All)FlotPairs()
* @param {array} required_times An array of timestamps that must be in the resulting pairs
* @return {array}
*/
ts.ZeroFilled.prototype.getFlotPairs = function (required_times) {
var times = this.getOrderedTimes(required_times),
strategy,
pairs;
if(this.opts.fill_style === 'all') {
strategy = this._getAllFlotPairs;
} else if(this.opts.fill_style === 'null') {
strategy = this._getNullFlotPairs;
} else {
strategy = this._getMinFlotPairs;
}
pairs = _.reduce(
times, // what
strategy, // how
[], // where
this // context
);
// if the first or last pair is inside either the start or end time,
// add those times to the series with null values so the graph will stretch to contain them.
// Removing, flot 0.8.1's max/min params satisfy this
/*
if (this.start_time && (pairs.length === 0 || pairs[0][0] > this.start_time)) {
pairs.unshift([this.start_time, null]);
}
if (this.end_time && (pairs.length === 0 || pairs[pairs.length - 1][0] < this.end_time)) {
pairs.push([this.end_time, null]);
}
*/
return pairs;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Fill zero's on either side of the current time, unless there is already a measurement there or
* we are looking at an edge.
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getMinFlotPairs = function (result, time, i, times) {
var next, expected_next, prev, expected_prev;
// check for previous measurement
if (i > 0) {
prev = times[i - 1];
expected_prev = this.interval.before(time);
if (prev < expected_prev) {
result.push([expected_prev, 0]);
}
}
// add the current time
result.push([ time, this._data[time] || 0]);
// check for next measurement
if (times.length > i) {
next = times[i + 1];
expected_next = this.interval.after(time);
if (next > expected_next) {
result.push([expected_next, 0]);
}
}
return result;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Fill zero's to the right of each time, until the next measurement is reached or we are at the
* last measurement
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getAllFlotPairs = function (result, time, i, times) {
var next, expected_next;
result.push([ times[i], this._data[times[i]] || 0 ]);
next = times[i + 1];
expected_next = this.interval.after(time);
for(; times.length > i && next > expected_next; expected_next = this.interval.after(expected_next)) {
result.push([expected_next, 0]);
}
return result;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Same as min, but fills with nulls
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getNullFlotPairs = function (result, time, i, times) {
var next, expected_next, prev, expected_prev;
// check for previous measurement
if (i > 0) {
prev = times[i - 1];
expected_prev = this.interval.before(time);
if (prev < expected_prev) {
result.push([expected_prev, null]);
}
}
// add the current time
result.push([ time, this._data[time] || null]);
// check for next measurement
if (times.length > i) {
next = times[i + 1];
expected_next = this.interval.after(time);
if (next > expected_next) {
result.push([expected_next, null]);
}
}
return result;
};
return ts;
});
\ No newline at end of file
<div class="editor-row">
<div class="section">
<h5>Values</h5>
<div class="editor-option">
<label class="small">Chart value</label>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total']"></select>
</div>
<div class="editor-option" ng-show="panel.mode != 'count'">
<label class="small">Value Field <tip>This field must contain a numeric value</tip></label>
<input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-large" ng-model="panel.value_field">
</div>
</div>
<div class="section">
<h5>Transform Series</h5>
<div class="editor-option" ng-show="panel.mode != 'count'">
<label class="small">Scale</label>
<input type="text" class="input-mini" ng-model="panel.scale">
</div>
<div class="editor-option">
<label class="small">Seconds <tip>Normalize intervals to per-second</tip></label><input type="checkbox" ng-model="panel.scaleSeconds" ng-checked="panel.scaleSeconds">
</div>
<div class="editor-option">
<label class="small">Derivative <tip>Plot the change per interval in the series</tip></label><input type="checkbox" ng-model="panel.derivative" ng-checked="panel.derivative" ng-change="set_refresh(true)">
</div>
</div>
</div>
<h5>Time Options</h5>
<div class="editor-row">
<div class="editor-option">
<label class="small">Time Field</label>
<input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.time_field">
</div>
<div class="editor-option">
<label class="small">Time correction</label>
<select ng-model="panel.timezone" class='input-small' ng-options="f for f in ['browser','utc']"></select>
</div>
<div class="editor-option">
<label class="small">Auto-interval</label><input type="checkbox" ng-model="panel.auto_int" ng-checked="panel.auto_int" />
</div>
<div class="editor-option" ng-show='panel.auto_int'>
<label class="small">Resolution <tip>Shoot for this many data points, rounding to sane intervals</tip></label>
<input type="number" class='input-mini' ng-model="panel.resolution" ng-change='set_refresh(true)'/>
</div>
<div class="editor-option" ng-hide='panel.auto_int'>
<label class="small">Interval <tip>Use Elasticsearch date math format (eg 1m, 5m, 1d, 2w, 1y)</tip></label>
<input type="text" class='input-mini' ng-model="panel.interval" ng-change='set_refresh(true)'/>
</div>
</div>
\ No newline at end of file
define([
'kbn'
],
function (kbn) {
'use strict';
/**
* manages the interval logic
* @param {[type]} interval_string An interval string in the format '1m', '1y', etc
*/
function Interval(interval_string) {
this.string = interval_string;
var info = kbn.describe_interval(interval_string);
this.type = info.type;
this.ms = info.sec * 1000 * info.count;
// does the length of the interval change based on the current time?
if (this.type === 'y' || this.type === 'M') {
// we will just modify this time object rather that create a new one constantly
this.get = this.get_complex;
this.date = new Date(0);
} else {
this.get = this.get_simple;
}
}
Interval.prototype = {
toString: function () {
return this.string;
},
after: function(current_ms) {
return this.get(current_ms, 1);
},
before: function (current_ms) {
return this.get(current_ms, -1);
},
get_complex: function (current, delta) {
this.date.setTime(current);
switch(this.type) {
case 'M':
this.date.setUTCMonth(this.date.getUTCMonth() + delta);
break;
case 'y':
this.date.setUTCFullYear(this.date.getUTCFullYear() + delta);
break;
}
return this.date.getTime();
},
get_simple: function (current, delta) {
return current + (delta * this.ms);
}
};
return Interval;
});
\ No newline at end of file
<div ng-controller='histogram' ng-init="init()" style="min-height:{{panel.height || row.height}}">
<style>
.histogram-legend {
display:inline-block;
padding-right:5px
}
.histogram-legend-dot {
display:inline-block;
height:10px;
width:10px;
border-radius:5px;
}
.histogram-legend-item {
display:inline-block;
}
.histogram-chart {
position:relative;
}
.histogram-options {
padding: 5px;
margin-right: 15px;
margin-bottom: 0px;
}
.histogram-options label {
margin: 0px 0px 0px 10px !important;
}
.histogram-options span {
white-space: nowrap;
}
/* this is actually should be in bootstrap */
.form-inline .checkbox {
display: inline-block;
}
</style>
<div>
<span ng-show='panel.options'>
<a class="link underline small" ng-show='panel.options' ng-click="options=!options">
<i ng-show="!options" class="icon-caret-right"></i><i ng-show="options" class="icon-caret-down"></i> View
</a> |&nbsp
</span>
<span ng-show='panel.zoomlinks && data'>
<!--<a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a>-->
<a class='small' ng-click='zoom(2)'><i class='icon-zoom-out'></i> Zoom Out</a> |&nbsp
</span>
<span ng-show="panel.legend" ng-repeat='series in data' class="histogram-legend">
<i class='icon-circle' ng-style="{color: series.info.color}"></i>
<span class='small histogram-legend-item'>
<span ng-if="panel.show_query">{{series.info.alias || series.info.query}}</span>
<span ng-if="!panel.show_query">{{series.info.alias}}</span>
<span ng-show="panel.legend_counts"> ({{series.hits}})</span>
</span>
</span>
<span ng-show="panel.legend" class="small"><span ng-show="panel.derivative">change in </span><span class="strong" ng-show="panel.value_field && panel.mode != 'count'">{{panel.value_field}}</span> {{panel.mode}} per <strong ng-hide="panel.scaleSeconds">{{panel.interval}}</strong><strong ng-show="panel.scaleSeconds">1s</strong> | (<strong>{{hits}}</strong> hits)</span>
</div>
<form class="form-inline bordered histogram-options" ng-show="options">
<span>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.bars" ng-checked="panel.bars" ng-change="render()">
Bars
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.lines" ng-checked="panel.lines" ng-change="render()">
Lines
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.stack" ng-checked="panel.stack" ng-change="render()">
Stack
</label>
</div>
</span>
<span ng-show="panel.stack">
<div class="checkbox">
<label style="white-space:nowrap" class="small">
<input type="checkbox" ng-model="panel.percentage" ng-checked="panel.percentage" ng-change="render()">
Percent
</label>
</div>
</span>
<span>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.legend" ng-checked="panel.legend" ng-change="render()">
Legend
</label>
</div>
</span>
<span>
<label class="small">Interval</label> <select ng-change="set_interval(panel.interval);get_data();" class="input-small" ng-model="panel.interval" ng-options="interval_label(time) for time in _.union([panel.interval],panel.intervals)"></select>
</span>
</form>
<center><img ng-show='panel.loading && _.isUndefined(data)' src="img/load_big.gif"></center>
<div histogram-chart class="pointer histogram-chart" params="{{panel}}"></div>
</div>
\ No newline at end of file
<h4>Charted</h4>
<div ng-include src="'app/partials/querySelect.html'"></div>
<div class="editor-row">
<h4>Markers</h4>
<div class="small">
Here you can specify a query to be plotted on your chart as a marker. Hovering over a marker will display the field you specify below. If more documents are found than the limit you set, they will be scored by Elasticsearch and events that best match your query will be displayed.
</div>
<style>
.querySelect .query {
margin-right: 5px;
}
.querySelect .selected {
border: 3px solid;
}
.querySelect .unselected {
border: 0px solid;
}
</style>
<p>
<div class="editor-option">
<label class="small">Enable</label>
<input type="checkbox" ng-change="set_refresh(true)" ng-model="panel.annotate.enable" ng-checked="panel.annotate.enable">
</div>
<div class="editor-option" ng-show="panel.annotate.enable">
<label class="small">Marker Query</label>
<input type="text" ng-change="set_refresh(true)" class="input-large" ng-model="panel.annotate.query"/>
</div>
<div class="editor-option" ng-show="panel.annotate.enable">
<label class="small">Tooltip field</label>
<input type="text" class="input-small" ng-model="panel.annotate.field" bs-typeahead="fields.list"/>
</div>
<div class="editor-option" ng-show="panel.annotate.enable">
<label class="small">Limit <tip>Max markers on the chart</tip></label>
<input type="number" class="input-mini" ng-model="panel.annotate.size" ng-change="set_refresh(true)"/>
</div>
<div class="editor-option" ng-show="panel.annotate.enable">
<label class="small">Sort <tip>Determine the most relevant markers using this field</tip></label>
<input type="text" class="input-small" bs-typeahead="fields.list" ng-model="panel.annotate.sort[0]" ng-change="set_refresh(true)" />
<i ng-click="panel.annotate.sort[1] = _.toggle(panel.annotate.sort[1],'desc','asc');set_refresh(true)" ng-class="{'icon-chevron-up': panel.annotate.sort[1] == 'asc','icon-chevron-down': panel.annotate.sort[1] == 'desc'}"></i>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Chart Options</h5>
<div class="editor-option">
<label class="small">Bars</label><input type="checkbox" ng-model="panel.bars" ng-checked="panel.bars">
</div>
<div class="editor-option">
<label class="small">Lines</label><input type="checkbox" ng-model="panel.lines" ng-checked="panel.lines">
</div>
<div class="editor-option">
<label class="small">Points</label><input type="checkbox" ng-model="panel.points" ng-checked="panel.points">
</div>
<div class="editor-option">
<label class="small">Selectable</label><input type="checkbox" ng-model="panel.interactive" ng-checked="panel.interactive">
</div>
<div class="editor-option">
<label class="small">xAxis</label><input type="checkbox" ng-model="panel['x-axis']" ng-checked="panel['x-axis']"></div>
<div class="editor-option">
<label class="small">yAxis</label><input type="checkbox" ng-model="panel['y-axis']" ng-checked="panel['y-axis']"></div>
<div class="editor-option" ng-show="panel.lines">
<label class="small">Line Fill</label>
<select class="input-mini" ng-model="panel.fill" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"></select>
</div>
<div class="editor-option" ng-show="panel.lines">
<label class="small">Line Width</label>
<select class="input-mini" ng-model="panel.linewidth" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"></select>
</div>
<div class="editor-option" ng-show="panel.points">
<label class="small">Point Radius</label>
<select class="input-mini" ng-model="panel.pointradius" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10]"></select>
</div>
<div class="editor-option">
<label class="small">Y Format <tip>Y-axis formatting</tip></label>
<select class="input-small" ng-model="panel.y_format" ng-options="f for f in ['none','short','bytes']"></select>
</div>
</div>
<div class="section">
<h5>Multiple Series</h5>
<div class="editor-option">
<label class="small">Stack</label><input type="checkbox" ng-model="panel.stack" ng-checked="panel.stack">
</div>
<div class="editor-option" ng-show="panel.stack">
<label style="white-space:nowrap" class="small">Percent <tip>Stack as a percentage of total</tip></label>
<input type="checkbox" ng-model="panel.percentage" ng-checked="panel.percentage">
</div>
<div class="editor-option" ng-show="panel.stack">
<label class="small">Stacked Values <tip>How should the values in stacked charts to be calculated?</tip></label>
<select class="input-small" ng-model="panel.tooltip.value_type" ng-options="f for f in ['cumulative','individual']"></select>
</div>
</div>
</div>
<div class="editor-row">
<div class="section">
<h5>Header<h5>
<div class="editor-option">
<label class="small">Zoom</label><input type="checkbox" ng-model="panel.zoomlinks" ng-checked="panel.zoomlinks" />
</div>
<div class="editor-option">
<label class="small">View</label><input type="checkbox" ng-model="panel.options" ng-checked="panel.options" />
</div>
</div>
<div class="section">
<h5>Legend<h5>
<div class="editor-option">
<label class="small">Legend</label><input type="checkbox" ng-model="panel.legend" ng-checked="panel.legend">
</div>
<div ng-show="panel.legend" class="editor-option">
<label class="small">Query <tip>If no alias is set, show the query in the legend</tip></label><input type="checkbox" ng-model="panel.show_query" ng-checked="panel.show_query">
</div>
<div ng-show="panel.legend" class="editor-option">
<label class="small">Counts</label><input type="checkbox" ng-model="panel.legend_counts" ng-checked="panel.legend_counts">
</div>
</div>
<div class="section">
<h5>Grid<h5>
<div class="editor-option">
<label class="small">Min / <a href='' ng-click="panel.grid.min = _.toggle(panel.grid.min,null,0)">Auto <i class="icon-star" ng-show="_.isNull(panel.grid.min)"></i></a></label>
<input type="number" class="input-small" ng-model="panel.grid.min"/>
</div>
<div class="editor-option">
<label class="small">Max / <a ref='' ng-click="panel.grid.max = _.toggle(panel.grid.max,null,0)">Auto <i class="icon-star" ng-show="_.isNull(panel.grid.max)"></i></a></label>
<input type="number" class="input-small" ng-model="panel.grid.max"/>
</div>
</div>
</div>
define([
'underscore',
'./interval'
],
function (_, Interval) {
'use strict';
var ts = {};
// map compatable parseInt
function base10Int(val) {
return parseInt(val, 10);
}
// trim the ms off of a time, but return it with empty ms.
function getDatesTime(date) {
return Math.floor(date.getTime() / 1000)*1000;
}
/**
* Certain graphs require 0 entries to be specified for them to render
* properly (like the line graph). So with this we will caluclate all of
* the expected time measurements, and fill the missing ones in with 0
* @param {object} opts An object specifying some/all of the options
*
* OPTIONS:
* @opt {string} interval The interval notion describing the expected spacing between
* each data point.
* @opt {date} start_date (optional) The start point for the time series, setting this and the
* end_date will ensure that the series streches to resemble the entire
* expected result
* @opt {date} end_date (optional) The end point for the time series, see start_date
* @opt {string} fill_style Either "minimal", or "all" describing the strategy used to zero-fill
* the series.
*/
ts.ZeroFilled = function (opts) {
opts = _.defaults(opts, {
interval: '10m',
start_date: null,
end_date: null,
fill_style: 'minimal'
});
// the expected differenece between readings.
this.interval = new Interval(opts.interval);
// will keep all values here, keyed by their time
this._data = {};
this.start_time = opts.start_date && getDatesTime(opts.start_date);
this.end_time = opts.end_date && getDatesTime(opts.end_date);
this.opts = opts;
};
/**
* Add a row
* @param {int} time The time for the value, in
* @param {any} value The value at this time
*/
ts.ZeroFilled.prototype.addValue = function (time, value) {
if (time instanceof Date) {
time = getDatesTime(time);
} else {
time = base10Int(time);
}
if (!isNaN(time)) {
this._data[time] = (_.isUndefined(value) ? 0 : value);
}
this._cached_times = null;
};
/**
* Get an array of the times that have been explicitly set in the series
* @param {array} include (optional) list of timestamps to include in the response
* @return {array} An array of integer times.
*/
ts.ZeroFilled.prototype.getOrderedTimes = function (include) {
var times = _.map(_.keys(this._data), base10Int);
if (_.isArray(include)) {
times = times.concat(include);
}
return _.uniq(times.sort(function (a, b) {
// decending numeric sort
return a - b;
}), true);
};
/**
* return the rows in the format:
* [ [time, value], [time, value], ... ]
*
* Heavy lifting is done by _get(Min|Default|All)FlotPairs()
* @param {array} required_times An array of timestamps that must be in the resulting pairs
* @return {array}
*/
ts.ZeroFilled.prototype.getFlotPairs = function (required_times) {
var times = this.getOrderedTimes(required_times),
strategy,
pairs;
if(this.opts.fill_style === 'all') {
strategy = this._getAllFlotPairs;
} else if(this.opts.fill_style === 'null') {
strategy = this._getNullFlotPairs;
} else {
strategy = this._getMinFlotPairs;
}
pairs = _.reduce(
times, // what
strategy, // how
[], // where
this // context
);
// if the first or last pair is inside either the start or end time,
// add those times to the series with null values so the graph will stretch to contain them.
// Removing, flot 0.8.1's max/min params satisfy this
/*
if (this.start_time && (pairs.length === 0 || pairs[0][0] > this.start_time)) {
pairs.unshift([this.start_time, null]);
}
if (this.end_time && (pairs.length === 0 || pairs[pairs.length - 1][0] < this.end_time)) {
pairs.push([this.end_time, null]);
}
*/
return pairs;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Fill zero's on either side of the current time, unless there is already a measurement there or
* we are looking at an edge.
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getMinFlotPairs = function (result, time, i, times) {
var next, expected_next, prev, expected_prev;
// check for previous measurement
if (i > 0) {
prev = times[i - 1];
expected_prev = this.interval.before(time);
if (prev < expected_prev) {
result.push([expected_prev, 0]);
}
}
// add the current time
result.push([ time, this._data[time] || 0]);
// check for next measurement
if (times.length > i) {
next = times[i + 1];
expected_next = this.interval.after(time);
if (next > expected_next) {
result.push([expected_next, 0]);
}
}
return result;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Fill zero's to the right of each time, until the next measurement is reached or we are at the
* last measurement
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getAllFlotPairs = function (result, time, i, times) {
var next, expected_next;
result.push([ times[i], this._data[times[i]] || 0 ]);
next = times[i + 1];
expected_next = this.interval.after(time);
for(; times.length > i && next > expected_next; expected_next = this.interval.after(expected_next)) {
result.push([expected_next, 0]);
}
return result;
};
/**
* ** called as a reduce stragegy in getFlotPairs() **
* Same as min, but fills with nulls
* @return {array} An array of points to plot with flot
*/
ts.ZeroFilled.prototype._getNullFlotPairs = function (result, time, i, times) {
var next, expected_next, prev, expected_prev;
// check for previous measurement
if (i > 0) {
prev = times[i - 1];
expected_prev = this.interval.before(time);
if (prev < expected_prev) {
result.push([expected_prev, null]);
}
}
// add the current time
result.push([ time, this._data[time] || null]);
// check for next measurement
if (times.length > i) {
next = times[i + 1];
expected_next = this.interval.after(time);
if (next > expected_next) {
result.push([expected_next, null]);
}
}
return result;
};
return ts;
});
\ No newline at end of file
...@@ -21,7 +21,7 @@ function (Settings) { ...@@ -21,7 +21,7 @@ function (Settings) {
* elasticsearch installed on. You probably want to set it to the FQDN of your * elasticsearch installed on. You probably want to set it to the FQDN of your
* elasticsearch host * elasticsearch host
*/ */
elasticsearch: "http://"+window.location.hostname+":9200", elasticsearch: "http://se0-elasticstash-01:9200",
/** @scratch /configuration/config.js/5 /** @scratch /configuration/config.js/5
* ==== kibana-int ==== * ==== kibana-int ====
...@@ -40,7 +40,8 @@ function (Settings) { ...@@ -40,7 +40,8 @@ function (Settings) {
panel_names: [ panel_names: [
'graph', 'graph',
'text', 'text',
'column' 'column',
'histogram'
] ]
}); });
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment