diff --git a/config.js b/config.js index f186a5da401..f03cc256efa 100644 --- a/config.js +++ b/config.js @@ -3,11 +3,8 @@ The settings before the break are the only ones that are currently implemented The remaining settings do nothing -timespan: Default timespan (eg 1d, 30d, 6h, 20m) -refresh: Milliseconds between auto refresh. +elasticsearch: URL to your elasticsearch server timeformat: Format for time in histograms (might go away) -timefield: Field to use for ISO8601 timestamps (might go away) -indexpattern: Timestamping pattern for time based indices, modules: Panel modules to load. In the future these will be inferred from your initial dashboard, though if you share dashboards you will probably need to list them all here @@ -17,22 +14,17 @@ NOTE: No timezone support yet, everything is in UTC at the moment. If you need to configure the default dashboard, please see dashboard.js shared.json contains an example sharable dashboard. Note the subtle differences -between dashboard.js and shared.json. Once is a javascript object, the other is +between dashboard.js and shared.json. One is a javascript object, the other is json. */ var config = new Settings( { - timespan: '15m', - refresh: 30000, elasticsearch: 'http://localhost:9200', timeformat: 'mm/dd HH:MM:ss', - timefield: '@timestamp', - indexpattern: '"shakespeare"', modules: ['histogram','map','pie','table','stringquery','sort', - 'timepicker','text'], + 'timepicker','text','fields','hits'], - defaultfields: ['line_text'], perpage: 50, timezone: 'user', operator: 'OR', diff --git a/dashboards.js b/dashboards.js index 54507e97968..323c37764f7 100644 --- a/dashboards.js +++ b/dashboards.js @@ -9,17 +9,18 @@ var dashboards = { type : "stringquery", span : 12, + group : ['default','counter','histogram'] } ] }, { - title: "Options", - collapse: true, - height: "30px", + title: "Status", + collapse: false, + height: "100px", panels: [ { type : "timepicker", - span : 5, + span : 4, mode : 'relative', index : "\"shakespeare\"", refresh : { @@ -33,28 +34,73 @@ var dashboards = }, { type : "sort", - span : 4, + span : 3, + }, + { + title : "Histogram Timer", + type : "timepicker", + span : 0, + mode : 'relative', + timespan : '5m', + index : "\"shakespeare\"", + refresh : { + enable : true, + interval: 30, + min : 10 + }, + timefield: '@timestamp', + group: 'histogram', + }, + { + type : "histogram", + span : 3, + show : ['lines'], + fill : 0.3, + group : "histogram", + query : [ + { label : "Event Rate", query : "*", color: '#FF7400' } + ], + }, + { + title : "Counter Timer", + type : "timepicker", + span : 0, + mode : 'relative', + timespan : '30d', + index : "\"shakespeare\"", + refresh : { + enable : true, + interval: 30, + min : 10 + }, + timefield: '@timestamp', + group: 'counter', + }, + { + type : "hits", + title : "Lines Completed", + span : 2, + group : 'counter', }, { type : "text", - fontsize : "85%", - span: 3, - content : "Panels can send events to other panels. In the case of" + - " the sort panel, it also receives a field event that it uses" + - " to populate its list of fields from the table panel. The time " + - " selector is a member of two groups." + style : {"font-size":"85%"}, + span: 0, + content : "Rows are collapsable, and input panels can send event to" + + " multiple groups. The Search panel is part of one group, while" + + " the time panel is part of two" }, ] }, { title: "Top 3 Characters", - collapse: false, - height: "160px", + collapse: true, + height: "150px", panels: [ { type : "text", title : "About", - fontsize : "85%", + style : {"font-size":"85%"}, span: 2, content : "These donut charts demonstrate configurable binding." + " They exist in a different group from the other panels and are" + @@ -136,14 +182,15 @@ var dashboards = }, { title: "Lines of Plays", - height: "250px", - collapse: true, + height: "210px", + collapse: false, panels: [ { title : "Plays", type : "pie", span : 4, size : 8, + labels : false, colors : ['#BF3030','#1D7373','#86B32D','#A60000','#006363','#679B00'], field : 'country', //query : { query: "*", field: "country"} @@ -152,10 +199,10 @@ var dashboards = { type : "text", title : "About", - fontsize : "85%", - span: 2, + style : {"font-size":"85%"}, + span: 0, content : "The table panel can be sorted via a sort panel, or by" + - " clicking the table header. Unlike the pie charts above, this" + + " clicking the table header. Unlike the donut charts above, this" + " pie is bound to the query input. Try searching for a speaker" + " (eg, FALSTAFF) to see a break down of the plays they appear in.", }, @@ -164,14 +211,20 @@ var dashboards = type : "table", span : 6, query : "*", + style : {"font-size":"85%"}, fields : ['@timestamp','play_name','speaker','text_entry'], - } + }, + { + type : "fields", + title : "Fields", + span : 2, + }, ] }, { title: "Monkey Monitoring", collapse: false, - height: "275px", + height: "225px", panels: [ { title : "Monkey Shakespeare Lines", @@ -197,7 +250,7 @@ var dashboards = { type : "text", title : "About", - fontsize : "85%", + style : {"font-size":"85%"}, span: 2, content : "Histograms can show multiple queries. In the case that a" + " multi-query histogram is bound to a query input, only the first" + diff --git a/js/controllers.js b/js/controllers.js index 049b49ec191..fc8b4512ad9 100644 --- a/js/controllers.js +++ b/js/controllers.js @@ -3,97 +3,17 @@ 'use strict'; angular.module('kibana.controllers', []) -.controller('DashCtrl', function($scope, $location, $http, $timeout, ejsResource) { - +.controller('DashCtrl', function($scope, ejsResource) { $scope.config = config; $scope.dashboards = dashboards - /* - $scope.timespan = config.timespan - $scope.time = { - from : time_ago($scope.timespan), - to : new Date() - } - - // I'm leaving in all this refresh stuff until I figure out how index - // list caching should work. Maybe it should be handled by each time panel? - // That would require dashboard to contain a time panel. Hmm. - $scope.counter = 0; - $scope.playing = true; - $scope.play = function(){ - $scope.counter++; - $scope.time.to = new Date(); - $scope.time.from = time_ago($scope.timespan); - $scope.$root.$eval() - mytimeout = $timeout($scope.play,config.refresh); - } - - $scope.pause = function(){ - if($scope.playing) { - $scope.playing = false; - $timeout.cancel(mytimeout); - } else { - $scope.playing = true; - mytimeout = $timeout($scope.play,config.refresh); - } - } - var mytimeout = $timeout($scope.play,config.refresh); - - // If from/to to change, update index list - $scope.$watch(function() { - return angular.toJson([$scope.time.from, $scope.time.to]) - }, function(){ - indices($scope.time.from,$scope.time.to).then(function (p) { - $scope.index = p.join(); - }); - }); - */ - - // point to your ElasticSearch server var ejs = $scope.ejs = ejsResource(config.elasticsearch); $scope.toggle_row = function(row) { $scope.$broadcast('toggle_row',row) row.collapse = row.collapse ? false : true; } - - $scope.set_timespan = function(timespan) { - $scope.timespan = timespan; - $scope.time.from = time_ago($scope.timespan); - } - - // returns a promise containing an array of all indices matching the index - // pattern that exist in a given range - function indices(from,to) { - var possible = []; - _.each(date_range(from,to.add_days(1)),function(d){ - possible.push(d.format(config.indexpattern)); - }); - - return all_indices().then(function(p) { - return _.intersection(p,possible); - }) - }; - - // returns a promise containing an array of all indices in an elasticsearch - // cluster - function all_indices() { - var something = $http({ - url: config.elasticsearch + "/_aliases", - method: "GET" - }).error(function(data, status, headers, config) { - $scope.error = status; - }); - - return something.then(function(p) { - var indices = []; - _.each(p.data, function(v,k) { - indices.push(k) - }); - return indices; - }); - } }); diff --git a/panels/fields/module.html b/panels/fields/module.html new file mode 100644 index 00000000000..7c5dafecd51 --- /dev/null +++ b/panels/fields/module.html @@ -0,0 +1,6 @@ +
+

{{panel.title}}

+ +
\ No newline at end of file diff --git a/panels/fields/module.js b/panels/fields/module.js new file mode 100644 index 00000000000..874ebc00197 --- /dev/null +++ b/panels/fields/module.js @@ -0,0 +1,39 @@ +angular.module('kibana.fields', []) +.controller('fields', function($scope, $rootScope) { + + var _id = _.uniqueId(); + + // Set and populate defaults + var _d = { + group : "default", + style : {"font-size":"85%","line-height":"15px"}, + } + _.defaults($scope.panel,_d); + + $scope.init = function() { + $scope.fields = []; + $scope.$on($scope.panel.group+"-fields", function(event, fields) { + $scope.panel.sort = _.clone(fields.sort); + $scope.fields = _.union(fields.all,$scope.fields); + $scope.active = _.clone(fields.active); + }); + } + + $scope.toggle_sort = function() { + $scope.panel.sort[1] = $scope.panel.sort[1] == 'asc' ? 'desc' : 'asc'; + } + + $scope.toggle_field = function(field) { + if (_.indexOf($scope.active,field) > -1) + $scope.active = _.without($scope.active,field) + else + $scope.active.push(field) + $rootScope.$broadcast($scope.panel.group+"-selected_fields",$scope.active) + } + + $scope.is_active = function(field) { + return _.indexOf($scope.active,field) > -1 ? 'active' : ''; + } + + $scope.init(); +}) \ No newline at end of file diff --git a/panels/histogram/module.js b/panels/histogram/module.js index a9008f38699..61d6e2f4e0f 100644 --- a/panels/histogram/module.js +++ b/panels/histogram/module.js @@ -39,8 +39,7 @@ angular.module('kibana.histogram', []) ejs.QueryStringQuery(v.query || '*'), ejs.RangeFilter($scope.panel.time.field) .from($scope.panel.time.from) - .to($scope.panel.time.to) - .cache(false)) + .to($scope.panel.time.to)) ) }); diff --git a/panels/hits/module.html b/panels/hits/module.html new file mode 100644 index 00000000000..ceea3d52d4c --- /dev/null +++ b/panels/hits/module.html @@ -0,0 +1,4 @@ +
+

{{panel.title}}

+

{{hits}}

+
\ No newline at end of file diff --git a/panels/hits/module.js b/panels/hits/module.js new file mode 100644 index 00000000000..f1b66ac6099 --- /dev/null +++ b/panels/hits/module.js @@ -0,0 +1,63 @@ +angular.module('kibana.hits', []) +.controller('hits', function($scope, $rootScope, $location) { + + var _id = _.uniqueId(); + + // Set and populate defaults + var _d = { + query : "*", + group : "default", + style : { "font-size": '36pt', "font-weight": "bold" }, + } + _.defaults($scope.panel,_d) + + $scope.init = function () { + $scope.$on(_id+"-time", function(event,time){set_time(time)}); + $scope.$on($scope.panel.group+"-time", function(event,time){set_time(time)}); + $scope.$on($scope.panel.group+"-query", function(event, query) { + $scope.panel.query = query; + $scope.get_data(); + }); + + // Now that we're all setup, request the time from our group + $rootScope.$broadcast($scope.panel.group+"-get_time",_id) + } + + $scope.get_data = function() { + // Make sure we have everything for the request to complete + if(_.isUndefined($scope.panel.index) || _.isUndefined($scope.panel.time)) + return + + var request = $scope.ejs.Request().indices($scope.panel.index); + + var results = request + .query(ejs.FilteredQuery( + ejs.QueryStringQuery($scope.panel.query || '*'), + ejs.RangeFilter(config.timefield) + .from($scope.panel.time.from) + .to($scope.panel.time.to) + ) + ) + .size(0) + .doSearch(); + + // Populate scope when we have results + results.then(function(results) { + if(_.isUndefined(results)) { + $scope.panel.error = 'Your query was unsuccessful'; + return; + } + $scope.panel.error = false; + $scope.hits = results.hits.total; + }); + } + + function set_time(time) { + $scope.panel.time = time; + $scope.panel.index = _.isUndefined(time.index) ? $scope.panel.index : time.index + $scope.get_data(); + } + + $scope.init(); + +}) diff --git a/panels/map/module.js b/panels/map/module.js index 36bf8f3450d..4d24e4fe801 100644 --- a/panels/map/module.js +++ b/panels/map/module.js @@ -44,7 +44,6 @@ angular.module('kibana.map', []) ejs.RangeFilter(config.timefield) .from($scope.panel.time.from) .to($scope.panel.time.to) - .cache(false) )))).size(0) .doSearch(); diff --git a/panels/pie/module.js b/panels/pie/module.js index f0937ae078a..5650181bd7f 100644 --- a/panels/pie/module.js +++ b/panels/pie/module.js @@ -48,8 +48,7 @@ angular.module('kibana.pie', []) ejs.QueryStringQuery(v.query || '*'), ejs.RangeFilter(config.timefield) .from($scope.panel.time.from) - .to($scope.panel.time.to) - .cache(false)) + .to($scope.panel.time.to)) ) }); @@ -176,11 +175,10 @@ angular.module('kibana.pie', []) function piett(x, y, contents) { var tooltip = $('#pie-tooltip').length ? $('#pie-tooltip') : $('
'); - //var tooltip = $('#pie-tooltip') tooltip.text(contents).css({ position: 'absolute', - top : y + 5, - left : x + 5, + top : y + 10, + left : x + 10, color : "#FFF", border : '1px solid #FFF', padding : '2px', diff --git a/panels/sort/module.html b/panels/sort/module.html index 44b9d8667cf..4a3a6b9db27 100644 --- a/panels/sort/module.html +++ b/panels/sort/module.html @@ -1,6 +1,6 @@

{{panel.title}}

- +
\ No newline at end of file diff --git a/panels/sort/module.js b/panels/sort/module.js index 836fe3e24d3..a145424ef95 100644 --- a/panels/sort/module.js +++ b/panels/sort/module.js @@ -16,13 +16,18 @@ angular.module('kibana.sort', []) $scope.init = function() { $scope.fields = []; $scope.$on($scope.panel.group+"-fields", function(event, fields) { - $scope.panel.sort = fields.sort; + $scope.panel.sort = _.clone(fields.sort); $scope.fields = _.union(fields.all,$scope.fields); }); } + $scope.set_sort = function() { + $rootScope.$broadcast($scope.panel.group+"-sort",$scope.panel.sort) + } + $scope.toggle_sort = function() { $scope.panel.sort[1] = $scope.panel.sort[1] == 'asc' ? 'desc' : 'asc'; + $rootScope.$broadcast($scope.panel.group+"-sort",$scope.panel.sort) } $scope.init(); }) \ No newline at end of file diff --git a/panels/stringquery/module.js b/panels/stringquery/module.js index 134da4ee4e5..33d5258a74b 100644 --- a/panels/stringquery/module.js +++ b/panels/stringquery/module.js @@ -13,10 +13,15 @@ angular.module('kibana.stringquery', []) } _.defaults($scope.panel,_d); + var _groups = _.isArray($scope.panel.group) ? + $scope.panel.group : [$scope.panel.group]; + $scope.init = function() { $scope.send_query = function(query) { - $rootScope.$broadcast($scope.panel.group+"-query", query) - } + _.each(_groups,function(group) { + $rootScope.$broadcast(group+"-query", query) + }); + } } $scope.init(); }); \ No newline at end of file diff --git a/panels/table/module.html b/panels/table/module.html index b5931887f8d..95f239e935b 100644 --- a/panels/table/module.html +++ b/panels/table/module.html @@ -1,11 +1,13 @@

{{panel.title}}

-
- +
+
- diff --git a/panels/table/module.js b/panels/table/module.js index 89abbd3b55c..e5a9e19c9f2 100644 --- a/panels/table/module.js +++ b/panels/table/module.js @@ -9,6 +9,8 @@ angular.module('kibana.table', []) size : 100, sort : ['@timestamp','desc'], group : "default", + style : {}, + fields : [], } _.defaults($scope.panel,_d) @@ -16,9 +18,17 @@ angular.module('kibana.table', []) $scope.$on(_id+"-time", function(event,time){set_time(time)}); $scope.$on($scope.panel.group+"-time", function(event,time){set_time(time)}); $scope.$on($scope.panel.group+"-query", function(event, query) { + console.log($scope.panel) $scope.panel.query = query; $scope.get_data(); }); + $scope.$on($scope.panel.group+"-sort", function(event,sort){ + $scope.panel.sort = _.clone(sort); + }); + $scope.$on($scope.panel.group+"-selected_fields", function(event, fields) { + $scope.panel.fields = _.clone(fields) + }); + $scope.$watch(function() { return angular.toJson($scope.panel.sort) }, function(){$scope.get_data()}); @@ -26,8 +36,11 @@ angular.module('kibana.table', []) $rootScope.$broadcast($scope.panel.group+"-get_time",_id) } - $scope.toggle_sort = function() { - $scope.panel.sort[1] = $scope.panel.sort[1] == 'asc' ? 'desc' : 'asc'; + $scope.set_sort = function(field) { + if($scope.panel.sort[0] === field) + $scope.panel.sort[1] = $scope.panel.sort[1] == 'asc' ? 'desc' : 'asc'; + else + $scope.panel.sort[0] = field; } $scope.get_data = function() { @@ -43,7 +56,6 @@ angular.module('kibana.table', []) ejs.RangeFilter(config.timefield) .from($scope.panel.time.from) .to($scope.panel.time.to) - .cache(false) ) ) .size($scope.panel.size) @@ -67,11 +79,16 @@ angular.module('kibana.table', []) $rootScope.$broadcast( $scope.panel.group+"-fields", { all : get_all_fields(results), - sort : $scope.panel.sort + sort : $scope.panel.sort, + active: $scope.panel.fields }); }); } + $scope.move_field = function(field,dir) { + console.log(field,dir) + } + function set_time(time) { $scope.panel.time = time; $scope.panel.index = _.isUndefined(time.index) ? $scope.panel.index : time.index @@ -80,4 +97,4 @@ angular.module('kibana.table', []) $scope.init(); -}) +}); \ No newline at end of file diff --git a/panels/text/module.html b/panels/text/module.html index 92458c0f6c0..e29f09d156c 100644 --- a/panels/text/module.html +++ b/panels/text/module.html @@ -1,4 +1,4 @@

{{panel.title}}

-

{{panel.content}}

+

{{panel.content}}

\ No newline at end of file diff --git a/panels/text/module.js b/panels/text/module.js index d0ae6b3db5f..82ee2e2d9a5 100644 --- a/panels/text/module.js +++ b/panels/text/module.js @@ -7,7 +7,7 @@ angular.module('kibana.text', []) var _d = { group : "default", content : "", - 'fontsize': "100%" + style: {}, } _.defaults($scope.panel,_d); diff --git a/partials/dashboard.html b/partials/dashboard.html index f180e29e7e9..f20d8812851 100644 --- a/partials/dashboard.html +++ b/partials/dashboard.html @@ -11,10 +11,10 @@
-
+
-
+
× Oops! {{panel.error}}
- {{field}} - + + + {{field}} + +