module.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /** @scratch /panels/5
  2. * include::panels/histogram.asciidoc[]
  3. */
  4. /** @scratch /panels/histogram/0
  5. * == Histogram
  6. * Status: *Stable*
  7. *
  8. * The histogram panel allow for the display of time charts. It includes several modes and tranformations
  9. * to display event counts, mean, min, max and total of numeric fields, and derivatives of counter
  10. * fields.
  11. *
  12. */
  13. define([
  14. 'angular',
  15. 'app',
  16. 'jquery',
  17. 'underscore',
  18. 'kbn',
  19. 'moment',
  20. './timeSeries',
  21. 'services/annotationsSrv',
  22. 'services/datasourceSrv',
  23. 'jquery.flot',
  24. 'jquery.flot.events',
  25. 'jquery.flot.selection',
  26. 'jquery.flot.time',
  27. 'jquery.flot.stack',
  28. 'jquery.flot.stackpercent'
  29. ],
  30. function (angular, app, $, _, kbn, moment, timeSeries) {
  31. 'use strict';
  32. var module = angular.module('kibana.panels.graph', []);
  33. app.useModule(module);
  34. module.controller('graph', function($scope, $rootScope, datasourceSrv, $timeout, annotationsSrv) {
  35. $scope.panelMeta = {
  36. modals : [],
  37. editorTabs: [],
  38. fullEditorTabs : [
  39. {
  40. title: 'General',
  41. src:'app/partials/panelgeneral.html'
  42. },
  43. {
  44. title: 'Metrics',
  45. src:'app/partials/metrics.html'
  46. },
  47. {
  48. title:'Axes & Grid',
  49. src:'app/panels/graph/axisEditor.html'
  50. },
  51. {
  52. title:'Display Styles',
  53. src:'app/panels/graph/styleEditor.html'
  54. }
  55. ],
  56. fullscreenEdit: true,
  57. fullscreenView: true,
  58. description : "Graphing"
  59. };
  60. // Set and populate defaults
  61. var _d = {
  62. datasource: null,
  63. /** @scratch /panels/histogram/3
  64. * renderer:: sets client side (flot) or native graphite png renderer (png)
  65. */
  66. renderer: 'flot',
  67. /** @scratch /panels/histogram/3
  68. * x-axis:: Show the x-axis
  69. */
  70. 'x-axis' : true,
  71. /** @scratch /panels/histogram/3
  72. * y-axis:: Show the y-axis
  73. */
  74. 'y-axis' : true,
  75. /** @scratch /panels/histogram/3
  76. * scale:: Scale the y-axis by this factor
  77. */
  78. scale : 1,
  79. /** @scratch /panels/histogram/3
  80. * y_formats :: 'none','bytes','bits','short', 's', 'ms'
  81. */
  82. y_formats : ['short', 'short'],
  83. /** @scratch /panels/histogram/5
  84. * grid object:: Min and max y-axis values
  85. * grid.min::: Minimum y-axis value
  86. * grid.ma1::: Maximum y-axis value
  87. */
  88. grid : {
  89. leftMax: null,
  90. rightMax: null,
  91. leftMin: null,
  92. rightMin: null,
  93. threshold1: null,
  94. threshold2: null,
  95. threshold1Color: 'rgba(216, 200, 27, 0.27)',
  96. threshold2Color: 'rgba(234, 112, 112, 0.22)'
  97. },
  98. annotate : {
  99. enable : false,
  100. },
  101. /** @scratch /panels/histogram/3
  102. * resolution:: If auto_int is true, shoot for this many bars.
  103. */
  104. resolution : 100,
  105. /** @scratch /panels/histogram/3
  106. * ==== Drawing options
  107. * lines:: Show line chart
  108. */
  109. lines : true,
  110. /** @scratch /panels/histogram/3
  111. * fill:: Area fill factor for line charts, 1-10
  112. */
  113. fill : 0,
  114. /** @scratch /panels/histogram/3
  115. * linewidth:: Weight of lines in pixels
  116. */
  117. linewidth : 1,
  118. /** @scratch /panels/histogram/3
  119. * points:: Show points on chart
  120. */
  121. points : false,
  122. /** @scratch /panels/histogram/3
  123. * pointradius:: Size of points in pixels
  124. */
  125. pointradius : 5,
  126. /** @scratch /panels/histogram/3
  127. * bars:: Show bars on chart
  128. */
  129. bars : false,
  130. /** @scratch /panels/histogram/3
  131. * stack:: Stack multiple series
  132. */
  133. stack : false,
  134. /** @scratch /panels/histogram/3
  135. * legend:: Display the legend
  136. */
  137. legend: {
  138. show: true, // disable/enable legend
  139. values: false, // disable/enable legend values
  140. min: false,
  141. max: false,
  142. current: false,
  143. total: false,
  144. avg: false
  145. },
  146. /** @scratch /panels/histogram/3
  147. * ==== Transformations
  148. /** @scratch /panels/histogram/3
  149. * percentage:: Show the y-axis as a percentage of the axis total. Only makes sense for multiple
  150. * queries
  151. */
  152. percentage : false,
  153. /** @scratch /panels/histogram/3
  154. * zerofill:: Improves the accuracy of line charts at a small performance cost.
  155. */
  156. zerofill : true,
  157. nullPointMode : 'connected',
  158. steppedLine: false,
  159. tooltip : {
  160. value_type: 'cumulative',
  161. query_as_alias: true
  162. },
  163. targets: [],
  164. aliasColors: {},
  165. aliasYAxis: {},
  166. };
  167. _.defaults($scope.panel,_d);
  168. _.defaults($scope.panel.tooltip, _d.tooltip);
  169. _.defaults($scope.panel.annotate, _d.annotate);
  170. _.defaults($scope.panel.grid, _d.grid);
  171. // backward compatible stuff
  172. if (_.isBoolean($scope.panel.legend)) {
  173. $scope.panel.legend = { show: $scope.panel.legend };
  174. _.defaults($scope.panel.legend, _d.legend);
  175. }
  176. if ($scope.panel.grid.min) {
  177. $scope.panel.grid.leftMin = $scope.panel.grid.min;
  178. delete $scope.panel.grid.min;
  179. }
  180. if ($scope.panel.grid.max) {
  181. $scope.panel.grid.leftMax = $scope.panel.grid.max;
  182. delete $scope.panel.grid.max;
  183. }
  184. if ($scope.panel.y_format) {
  185. $scope.panel.y_formats[0] = $scope.panel.y_format;
  186. delete $scope.panel.y_format;
  187. }
  188. if ($scope.panel.y2_format) {
  189. $scope.panel.y_formats[1] = $scope.panel.y2_format;
  190. delete $scope.panel.y2_format;
  191. }
  192. $scope.init = function() {
  193. $scope.initBaseController(this, $scope);
  194. $scope.fullscreen = false;
  195. $scope.editor = { index: 1 };
  196. $scope.editorTabs = _.pluck($scope.panelMeta.fullEditorTabs,'title');
  197. $scope.hiddenSeries = {};
  198. $scope.datasources = datasourceSrv.listOptions();
  199. $scope.setDatasource($scope.panel.datasource);
  200. if ($scope.panel.targets.length === 0) {
  201. $scope.panel.targets.push({});
  202. }
  203. };
  204. $scope.setDatasource = function(datasource) {
  205. $scope.panel.datasource = datasource;
  206. $scope.datasource = datasourceSrv.get(datasource);
  207. if (!$scope.datasource) {
  208. $scope.panel.error = "Cannot find datasource " + datasource;
  209. return;
  210. }
  211. $scope.get_data();
  212. };
  213. $scope.removeTarget = function (target) {
  214. $scope.panel.targets = _.without($scope.panel.targets, target);
  215. $scope.get_data();
  216. };
  217. $scope.updateTimeRange = function () {
  218. $scope.range = this.filter.timeRange();
  219. $scope.rangeUnparsed = this.filter.timeRange(false);
  220. $scope.resolution = Math.ceil($(window).width() * ($scope.panel.span / 12));
  221. $scope.interval = '10m';
  222. if ($scope.range) {
  223. $scope.interval = kbn.secondsToHms(
  224. kbn.calculate_interval($scope.range.from, $scope.range.to, $scope.resolution, 0) / 1000
  225. );
  226. }
  227. };
  228. $scope.get_data = function() {
  229. delete $scope.panel.error;
  230. $scope.panelMeta.loading = true;
  231. $scope.updateTimeRange();
  232. var graphiteQuery = {
  233. range: $scope.rangeUnparsed,
  234. interval: $scope.interval,
  235. targets: $scope.panel.targets,
  236. format: $scope.panel.renderer === 'png' ? 'png' : 'json',
  237. maxDataPoints: $scope.resolution,
  238. datasource: $scope.panel.datasource
  239. };
  240. $scope.annotationsPromise = annotationsSrv.getAnnotations($scope.filter, $scope.rangeUnparsed);
  241. return $scope.datasource.query($scope.filter, graphiteQuery)
  242. .then($scope.dataHandler)
  243. .then(null, function(err) {
  244. $scope.panelMeta.loading = false;
  245. $scope.panel.error = err.message || "Timeseries data request error";
  246. $scope.inspector.error = err;
  247. $scope.render([]);
  248. });
  249. };
  250. $scope.dataHandler = function(results) {
  251. $scope.panelMeta.loading = false;
  252. $scope.legend = [];
  253. // png renderer returns just a url
  254. if (_.isString(results)) {
  255. $scope.render(results);
  256. return;
  257. }
  258. $scope.datapointsWarning = false;
  259. $scope.datapointsCount = 0;
  260. $scope.datapointsOutside = false;
  261. var data = _.map(results.data, $scope.seriesHandler);
  262. $scope.datapointsWarning = $scope.datapointsCount || !$scope.datapointsOutside;
  263. $scope.annotationsPromise
  264. .then(function(annotations) {
  265. data.annotations = annotations;
  266. $scope.render(data);
  267. }, function() {
  268. $scope.render(data);
  269. });
  270. };
  271. $scope.seriesHandler = function(seriesData, index) {
  272. var datapoints = seriesData.datapoints;
  273. var alias = seriesData.target;
  274. var color = $scope.panel.aliasColors[alias] || $scope.colors[index];
  275. var yaxis = $scope.panel.aliasYAxis[alias] || 1;
  276. var seriesInfo = {
  277. alias: alias,
  278. color: color,
  279. enable: true,
  280. yaxis: yaxis
  281. };
  282. $scope.legend.push(seriesInfo);
  283. var series = new timeSeries.ZeroFilled({
  284. datapoints: datapoints,
  285. info: seriesInfo,
  286. });
  287. if (datapoints && datapoints.length > 0) {
  288. var last = moment.utc(datapoints[datapoints.length - 1][1] * 1000);
  289. var from = moment.utc($scope.range.from);
  290. if (last - from < -10000) {
  291. $scope.datapointsOutside = true;
  292. }
  293. $scope.datapointsCount += datapoints.length;
  294. }
  295. return series;
  296. };
  297. $scope.add_target = function() {
  298. $scope.panel.targets.push({target: ''});
  299. };
  300. $scope.otherPanelInFullscreenMode = function() {
  301. return $rootScope.fullscreen && !$scope.fullscreen;
  302. };
  303. $scope.render = function(data) {
  304. $scope.$emit('render', data);
  305. };
  306. $scope.changeSeriesColor = function(series, color) {
  307. series.color = color;
  308. $scope.panel.aliasColors[series.alias] = series.color;
  309. $scope.render();
  310. };
  311. $scope.toggleSeries = function(serie, event) {
  312. if ($scope.hiddenSeries[serie.alias]) {
  313. delete $scope.hiddenSeries[serie.alias];
  314. }
  315. else {
  316. $scope.hiddenSeries[serie.alias] = true;
  317. }
  318. if (event.ctrlKey || event.metaKey || event.shiftKey) {
  319. $scope.toggleSeriesExclusiveMode(serie);
  320. }
  321. $scope.$emit('toggleLegend', $scope.legend);
  322. };
  323. $scope.toggleSeriesExclusiveMode = function(serie) {
  324. var hidden = $scope.hiddenSeries;
  325. if (hidden[serie.alias]) {
  326. delete hidden[serie.alias];
  327. }
  328. // check if every other series is hidden
  329. var alreadyExclusive = _.every($scope.legend, function(value) {
  330. if (value.alias === serie.alias) {
  331. return true;
  332. }
  333. return hidden[value.alias];
  334. });
  335. if (alreadyExclusive) {
  336. // remove all hidden series
  337. _.each($scope.legend, function(value) {
  338. delete $scope.hiddenSeries[value.alias];
  339. });
  340. }
  341. else {
  342. // hide all but this serie
  343. _.each($scope.legend, function(value) {
  344. if (value.alias === serie.alias) {
  345. return;
  346. }
  347. $scope.hiddenSeries[value.alias] = true;
  348. });
  349. }
  350. };
  351. $scope.toggleYAxis = function(info) {
  352. info.yaxis = info.yaxis === 2 ? 1 : 2;
  353. $scope.panel.aliasYAxis[info.alias] = info.yaxis;
  354. $scope.render();
  355. };
  356. $scope.toggleGridMinMax = function(key) {
  357. $scope.panel.grid[key] = _.toggle($scope.panel.grid[key], null, 0);
  358. $scope.render();
  359. };
  360. });
  361. });