Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timezone correction across panels #341

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion common/lib/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
// THESE ARE ONLY DEFAULTS. They are overridden by config.js in the root directory
var Settings = function (s) {
var _d = {
elasticsearch_servers : ["http://"+window.location.hostname+":9200"],
elasticsearch : "http://"+window.location.hostname+":9200",
modules : [],
kibana_index : 'kibana-int'
kibana_index : 'kibana-int',
timezone_path: "tz",
default_zone_file: []
}

// This initializes a new hash on purpose, to avoid adding parameters to
Expand Down
11 changes: 10 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ If you need to configure the default dashboard, please see dashboards/default
*/
var config = new Settings(
{
// By default this will attempt to reach ES at the same host you have
//Specify all the instances of elasticsearch servers. This will be displayed in dashboard configuration.
//Please note that issue of cross domain origin request. To address that you can use apache proxy pass
//For example, if i want es2 instance i can setup something like this:
//ProxyPassMatch ^(/es2)(/_aliases|.*/_search|.*/_mapping)$ http://192.168.1.2/$2
//and in the following config i can add http://mydomain/es2
elasticsearch_servers: ["http://"+window.location.hostname+":9200"],
//By default this will attempt to reach ES at the same host you have
// elasticsearch installed on. You probably want to set it to the FQDN of your
// elasticsearch host
elasticsearch: "http://"+window.location.hostname+":9200",
Expand All @@ -24,5 +30,8 @@ var config = new Settings(
'timepicker','text','fields','hits','dashcontrol',
'column','derivequeries','trends','bettermap','query',
'terms'],
timezone_path: "tz",
//specify what all zone files to load by default eg. ['asia', 'africa']
default_zone_file: [],
}
);
4 changes: 3 additions & 1 deletion dashboards/logstash.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"title": "Logstash Search",
"timezone": "browser",
"services": {
"query": {
"idQueue": [
Expand Down Expand Up @@ -171,7 +172,6 @@
"interval": "30s",
"fill": 3,
"linewidth": 3,
"timezone": "browser",
"spyable": true,
"zoomlinks": true,
"bars": true,
Expand Down Expand Up @@ -235,6 +235,8 @@
]
},
"field_list": true,
"timeFormatShort" : "yyyy-MM-dd HH:mm:ss",
"timeFormatLong" : "yyyy-MM-dd HH:mm:ss",
"status": "Stable"
}
]
Expand Down
4 changes: 4 additions & 0 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var labjs = $LAB
.script("js/controllers.js")
.script("js/filters.js")
.script("js/directives.js")
.script("common/lib/panels/timezone.js")
.script("js/panels.js").wait();

_.each(config.modules, function(v) {
Expand All @@ -59,6 +60,9 @@ labjs.wait(function(){
});
}]);
angular.element(document).ready(function() {
timezoneJS.timezone.zoneFileBasePath = config.timezone_path;
timezoneJS.timezone.defaultZoneFile = config.default_zone_file;
timezoneJS.timezone.init();
$('body').attr('ng-controller', 'DashCtrl');
angular.bootstrap(document, ['kibana']);
});
Expand Down
4 changes: 3 additions & 1 deletion js/controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ angular.module('kibana.controllers', [])
editable: true,
rows: [],
last: null,
style: 'dark'
style: 'dark',
timezone: 'browser',
};

$scope.editor = {
Expand All @@ -23,6 +24,7 @@ angular.module('kibana.controllers', [])
$scope.config = config;
// Make underscore.js available to views
$scope._ = _;
$scope.timezoneJS = timezoneJS;
$scope.dashboard = dashboard;

// Provide a global list of all see fields
Expand Down
9 changes: 8 additions & 1 deletion js/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ angular.module('kibana.services', [])
var self = this;

this.init = function() {
ejs = ejsResource(config.elasticsearch);
_q = dashboard.current.services.query;
self.list = dashboard.current.services.query.list;
self.ids = dashboard.current.services.query.ids;
Expand Down Expand Up @@ -269,6 +270,7 @@ angular.module('kibana.services', [])

// Call this whenever we need to reload the important stuff
this.init = function() {
ejs = ejsResource(config.elasticsearch);
// Accessors
self.list = dashboard.current.services.filter.list;
self.ids = dashboard.current.services.filter.ids;
Expand Down Expand Up @@ -442,7 +444,8 @@ angular.module('kibana.services', [])
index: {
interval: 'none',
pattern: '_all',
default: 'INDEX_MISSING'
default: 'INDEX_MISSING',
server: config.elasticsearch
},
};

Expand Down Expand Up @@ -503,6 +506,10 @@ angular.module('kibana.services', [])
// Since the dashboard is responsible for index computation, we can compute and assign the indices
// here before telling the panels to refresh
this.refresh = function() {
if (self.current.index.server != undefined) {
config.elasticsearch = self.current.index.server;
ejs = ejsResource(config.elasticsearch);
}
if(self.current.index.interval !== 'none') {
if(filterSrv.idsByType('time').length > 0) {
var _range = filterSrv.timeRange('min');
Expand Down
4 changes: 0 additions & 4 deletions panels/histogram/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ <h5>Chart Settings</h5>
</div>
</div>
<div class="row-fluid">
<div class="span2">
<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="span1"> <label class="small">Selectable</label><input type="checkbox" ng-model="panel.interactive" ng-checked="panel.interactive"></div>
<div class="span2">
<label class="small">Zoom Links</label><input type="checkbox" ng-model="panel.zoomlinks" ng-checked="panel.zoomlinks" />
Expand Down
9 changes: 2 additions & 7 deletions panels/histogram/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
* linewidth :: Only applies to line charts. How thick the line should be in pixels
While the editor only exposes 0-10, this can be any numeric value.
Set to 0 and you'll get something like a scatter plot
* timezone :: This isn't totally functional yet. Currently only supports browser and utc.
browser will adjust the x-axis labels to match the timezone of the user's
browser
* spyable :: Dislay the 'eye' icon that show the last elasticsearch query
* zoomlinks :: Show the zoom links?
* bars :: Show bars in the chart
Expand Down Expand Up @@ -57,7 +54,6 @@ angular.module('kibana.histogram', [])
interval : '5m',
fill : 0,
linewidth : 3,
timezone : 'browser', // browser, utc or a standard timezone
spyable : true,
zoomlinks : true,
bars : true,
Expand Down Expand Up @@ -294,8 +290,7 @@ angular.module('kibana.histogram', [])
var scripts = $LAB.script("common/lib/panels/jquery.flot.js").wait()
.script("common/lib/panels/jquery.flot.time.js")
.script("common/lib/panels/jquery.flot.stack.js")
.script("common/lib/panels/jquery.flot.selection.js")
.script("common/lib/panels/timezone.js");
.script("common/lib/panels/jquery.flot.selection.js");

// Populate element. Note that jvectormap appends, does not replace.
scripts.wait(function(){
Expand Down Expand Up @@ -324,7 +319,7 @@ angular.module('kibana.histogram', [])
max: scope.panel.percentage && scope.panel.stack ? 100 : null,
},
xaxis: {
timezone: scope.panel.timezone,
timezone: dashboard.current.timezone,
show: scope.panel['x-axis'],
mode: "time",
min: _.isUndefined(scope.range.from) ? null : scope.range.from.getTime(),
Expand Down
14 changes: 14 additions & 0 deletions panels/table/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ <h6>Trim Factor <i class="icon-question-sign" bs-tooltip="'Trim fields to this l
<input type="number" class="input-small" ng-model="panel.trimFactor">
</div>
</div>
<div class="row-fluid">
<div class="span3" style="white-space:nowrap">
<h6>Time Field</h6>
<select ng-show="all_fields.length>0"style="width:85%" ng-model="panel.timeField" ng-options="f for f in all_fields"></select>
</div>
<div class="span4">
<h6>Time Display Format (Short) <i class="icon-question-sign" bs-tooltip="'This format will be used for displaying the time field in table.<br /><br />y : year<br />m : minute<br />s: second<br />S: millisecond<br />h: 12 hour format<br />MM: month (numeric representation)<br />MMM: month (abbrevated text representation)<br />MMMM: month (full text representation)<br />k: AM/PM<br />H: hour<br />E: day<br />Z: timezone abbreviation<br />'"></i></h6>
<input type="text" ng-model="panel.timeFormatShort" />
</div>
<div class="span4">
<h6>Time Display Format (Long) <i class="icon-question-sign" bs-tooltip="'This format will be used for displaying the time field in details.<br /><br />y : year<br />m : minute<br />s: second<br />S: millisecond<br />h: 12 hour format<br />MM: month (numeric representation)<br />MMM: month (abbrevated text representation)<br />MMMM: month (full text representation)<br />k: AM/PM<br />H: hour<br />E: day<br />Z: timezone abbreviation<br />'"></i></h6>
<input type="text" ng-model="panel.timeFormatLong" />
</div>
</div>
<h5>Paging</h5>
<div class="row-fluid">
<div class="span2">
Expand Down
6 changes: 4 additions & 2 deletions panels/table/module.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ <h5>Fields <i class=" icon-chevron-sign-left pointer " ng-click="panel.field_lis
</thead>
<tbody ng-repeat="row in data | slice:panel.offset:panel.offset+panel.size" ng-class-odd="'odd'">
<tr ng-click="toggle_details(row)" class="pointer">
<td ng-repeat="field in panel.fields" ng-bind-html-unsafe="(row.highlight[field]||row._source[field]) | tableHighlight | tableTruncate:panel.trimFactor:panel.fields.length"></td>
<td ng-repeat="field in panel.fields" ng-show="field == panel.timeField" ng-bind-html-unsafe="(row.highlight[field]||row._source[field]) | tableHighlight | datetz:dashboard.current.timezone:panel.timeFormatShort | tableTruncate:panel.trimFactor:panel.fields.length"></td>
<td ng-repeat="field in panel.fields" ng-show="field != panel.timeField" ng-bind-html-unsafe="(row.highlight[field]||row._source[field]) | tableHighlight | tableTruncate:panel.trimFactor:panel.fields.length"></td>
</tr>
<tr ng-show="row.kibana.details">
<td colspan=1000>
Expand All @@ -73,7 +74,8 @@ <h5>Fields <i class=" icon-chevron-sign-left pointer " ng-click="panel.field_lis
<i class='icon-search pointer' ng-click="build_search(key,value)"></i>
<i class='icon-ban-circle pointer' ng-click="build_search(key,value,true)"></i>
</td>
<td>{{value}}</td>
<td ng-show="key == panel.timeField">{{value | datetz:dashboard.current.timezone:panel.timeFormatLong}}</td>
<td ng-show="key != panel.timeField">{{value}}</td>
</tr>
</table>
</td>
Expand Down
27 changes: 26 additions & 1 deletion panels/table/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ angular.module('kibana.table', [])
paging : true,
field_list: true,
trimFactor: 300,
spyable : true
spyable : true,
timeField : "@timestamp",
timeFormatShort : "yyyy-MM-dd HH:mm:ss",
timeFormatLong : "yyyy-MM-dd HH:mm:ss"
};
_.defaults($scope.panel,_d);

Expand Down Expand Up @@ -272,4 +275,26 @@ angular.module('kibana.table', [])
}
return '';
};
}).filter('datetz', function () {
return function (text, zone, format) {
try {
var dt = new timezoneJS.Date(text);
if (format === undefined) {
format = 'yyyy-MM-dd HH:mm:ss';
}
if (zone === undefined) {
zone = "browser";
} else if (zone == "utc") {
zone = "Etc/UTC";
}

if (zone != "browser") {
dt.setTimezone(zone);
}
return dt.toString(format);
} catch (e) {
return text;
}
//console.log(dt);
};
});
71 changes: 63 additions & 8 deletions panels/timepicker/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ angular.module('kibana.timepicker', [])

$scope.to_now = function() {
$scope.timepicker.to = {
time : moment().format("HH:mm:ss"),
date : moment().format("MM/DD/YYYY")
time : convertTimeZone(moment().utc(), dashboard.current.timezone, "HH:mm:ss", "utc"),
date : convertTimeZone(moment().utc(), dashboard.current.timezone, "MM/dd/yyyy", "utc")
};
};

Expand All @@ -186,9 +186,9 @@ angular.module('kibana.timepicker', [])
// If time picker is defined (usually is) TOFIX: Horrible parsing
if(!(_.isUndefined($scope.timepicker))) {
from = $scope.panel.mode === 'relative' ? moment(kbn.time_ago($scope.panel.timespan)) :
moment(moment.utc($scope.timepicker.from.date).format('MM/DD/YYYY') + " " + $scope.timepicker.from.time,'MM/DD/YYYY HH:mm:ss');
moment.utc(convertTimeZone(moment($scope.timepicker.from.date).format('MM/DD/YYYY') + " " + $scope.timepicker.from.time + " " + getTimeZoneOffset(dashboard.current.timezone), "utc", "MM/dd/yyyy HH:mm:ss", dashboard.current.timezone), 'MM/DD/YYYY HH:mm:ss');
to = $scope.panel.mode !== 'absolute' ? moment() :
moment(moment.utc($scope.timepicker.to.date).format('MM/DD/YYYY') + " " + $scope.timepicker.to.time,'MM/DD/YYYY HH:mm:ss');
moment.utc(convertTimeZone(moment($scope.timepicker.to.date).format('MM/DD/YYYY') + " " + $scope.timepicker.to.time + " " + getTimeZoneOffset(dashboard.current.timezone), "utc", "MM/dd/yyyy HH:mm:ss", dashboard.current.timezone), 'MM/DD/YYYY HH:mm:ss');
// Otherwise (probably initialization)
} else {
from = $scope.panel.mode === 'relative' ? moment(kbn.time_ago($scope.panel.timespan)) :
Expand Down Expand Up @@ -251,14 +251,69 @@ angular.module('kibana.timepicker', [])
// Janky 0s timeout to get around $scope queue processing view issue
$scope.timepicker = {
from : {
time : from.format("HH:mm:ss"),
date : from.format("MM/DD/YYYY")
time : convertTimeZone(from.format(), dashboard.current.timezone, "HH:mm:ss", "utc"),
date : convertTimeZone(from.format(), dashboard.current.timezone, "MM/dd/yyyy", "utc")
},
to : {
time : to.format("HH:mm:ss"),
date : to.format("MM/DD/YYYY")
time : convertTimeZone(to.format(), dashboard.current.timezone, "HH:mm:ss", "utc"),
date : convertTimeZone(to.format(), dashboard.current.timezone, "MM/dd/yyyy", "utc")
}
};
}

function getTimeZoneOffset(zone) {
var dt = new timezoneJS.Date();
if (zone == "utc") {
dt.setTimezone("Etc/UTC");
} else if (zone == "browser") {
//do nothing
} else {
dt.setTimezone(zone);
}
var offset = dt.getTimezoneOffset();
var convertedOffset = (offset > 0) ? "-" : "+";
var offsetHour = Math.abs(parseInt(offset/60));
if (offsetHour.toString().length < 2) {
offsetHour = '0' + offsetHour;
}
var offsetMinute = Math.abs(parseInt(offset%60));
if (offsetMinute.toString().length < 2) {
offsetMinute = '0' + offsetMinute;
}
convertedOffset += offsetHour;
convertedOffset += offsetMinute;
return convertedOffset;
}

function convertTimeZone(text, zone, format, fromZone) {
try {

if (format === undefined) {
format = 'yyyy-MM-dd HH:mm:ss';
}
if (zone === undefined) {
zone = "browser";
} else if (zone == "utc") {
zone = "Etc/UTC";
}
var dt;
if (fromZone == "utc") {
dt = new timezoneJS.Date(text, "Etc/UTC");
} else if (fromZone == "browser" || fromZone == undefined) {
dt = new timezoneJS.Date(text);
} else {
dt = new timezoneJS.Date(text, fromZone);
}

if (zone != "browser") {
dt.setTimezone(zone);
}
return dt.toString(format);
} catch (e) {
return text;
}
}



});
15 changes: 12 additions & 3 deletions partials/dasheditor.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
<div class="span4">
<label class="small">Title</label><input type="text" class="input-large" ng-model='dashboard.current.title'></input>
</div>
<div class="span4">
<label class="small">Time Zone</label>
<select ng-model='dashboard.current.timezone' ng-options="tz for tz in ['browser', 'utc'].concat(timezoneJS.timezone.getAllZones())">
</select>
</div>
<div class="span1">
<label class="small"> Editable </label><input type="checkbox" ng-model="dashboard.current.editable" ng-checked="dashboard.current.editable" />
</div>
Expand Down Expand Up @@ -39,14 +44,18 @@ <h4>Index Settings</h4>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<div class="span2">
<h6>Timestamping</h6><select class="input-small" ng-model="dashboard.current.index.interval" ng-options='f for f in ["none","hour","day","week","month","year"]'></select>
</div>
<div class="span5" ng-show="dashboard.current.index.interval != 'none'">
<div class="span3">
<h6>Elastic Server</h6>
<select class="input-medium" ng-model='dashboard.current.index.server' ng-options="server for server in config.elasticsearch_servers"></select>
</div>
<div class="span3" ng-show="dashboard.current.index.interval != 'none'">
<h6>Index pattern <small>Absolutes in []</small></h6>
<input type="text" class="input-medium" ng-model="dashboard.current.index.pattern">
</div>
<div class="span4">
<div class="span3">
<h6>Default Index <small ng-show="dashboard.current.index.interval != 'none'">If index not found</small></h6>
<input type="text" class="input-medium" ng-model="dashboard.current.index.default">
</div>
Expand Down
Loading