module.js 9.9 KB

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