diff --git a/api/metrics-api-jaxrs/src/main/webapp/static/css/main.css b/api/metrics-api-jaxrs/src/main/webapp/static/css/main.css index 6e43655b3..4527b9702 100644 --- a/api/metrics-api-jaxrs/src/main/webapp/static/css/main.css +++ b/api/metrics-api-jaxrs/src/main/webapp/static/css/main.css @@ -1,19 +1,3 @@ -/* - * Copyright 2014-2017 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ .navbar .brand-name { font-size: 18pt; padding-top: 15px; @@ -27,6 +11,80 @@ color: #f5f5f5; } +.navbar-sidebar { + background: #292e33; + border: 0; + border-radius: 0; + bottom: 0; + left: -240px; + margin: 0; + padding: 0; + position: fixed; + top: 58px; + width: 240px; + z-index: 1029; +} + +@media (min-width: 992px) { + .navbar-sidebar { + box-shadow: none!important; + left: 0; + overflow-x: hidden; + overflow-y: auto; + padding-right: 0; + } +} + +.navbar-sidebar { + text-align: initial; +} + +.navbar-sidebar .section { + color: #fff; + display: block; + padding: 0 20px 20px 20px; + border-bottom: 1px solid #393f44; + margin-bottom: 10px; +} + +.navbar-sidebar .title { + font-size: 16px; + line-height: 60px; +} + +.navbar-sidebar ul { + list-style: none; + margin: 0; + padding: 0; +} + +.navbar-sidebar ul li a { + text-decoration: none; + cursor: pointer; + color: #fff; + line-height: 24px; +} + +.navbar-sidebar input { + width: 200px; + color: #363636; +} + +.container-fluid { + padding-left: 45px; +} + +.container-fluid { + padding-bottom: 50px; + padding-top: 58px; +} + +@media (min-width: 992px) { + .container-fluid { + padding-left: 260px; + } +} + .content { background-color: #f5f5f5; width: 100%; @@ -53,3 +111,7 @@ h1, h3 { text-align: center; word-wrap:break-word; } + +#line-chart { + margin-top: 50px; +} diff --git a/api/metrics-api-jaxrs/src/main/webapp/static/index.html b/api/metrics-api-jaxrs/src/main/webapp/static/index.html index f19139285..927eabc7d 100644 --- a/api/metrics-api-jaxrs/src/main/webapp/static/index.html +++ b/api/metrics-api-jaxrs/src/main/webapp/static/index.html @@ -1,3 +1,20 @@ + + Hawkular Metrics @@ -5,12 +22,18 @@ - + --> + + + + + + diff --git a/api/metrics-api-jaxrs/src/main/webapp/static/js/navigation.js b/api/metrics-api-jaxrs/src/main/webapp/static/js/navigation.js index 3800d3838..06c56ad27 100644 --- a/api/metrics-api-jaxrs/src/main/webapp/static/js/navigation.js +++ b/api/metrics-api-jaxrs/src/main/webapp/static/js/navigation.js @@ -21,11 +21,159 @@ const { Router, hashHistory, Link } = ReactRouter; -const Metrics = () => ( -
-

Metrics

-
-) +class Chart extends React.Component { + constructor(props) { + super(props); + console.log("ctor Chart"); + } + + render() { + console.log("render Chart"); + let title, notice; + if (this.props.tenant && this.props.type && this.props.metric) { + title = "Tenant '" + this.props.tenant + "', " + this.props.type + " '" + this.props.metric + "'"; + } else if (this.props.tenant) { + title = "Tenant '" + this.props.tenant + "'"; + notice = "Select a metric from the left menu" + } else { + notice = "Select a tenant and a metric from the left menu" + } + return ( +
+

{title}

{notice}

+
+
+ ); + } + + componentDidMount() { + console.log("did mount Chart"); + const config = $().c3ChartDefaults().getDefaultSingleLineConfig(); + config.bindto = '#line-chart'; + config.data = { + x: 'x', + columns: [['x'],['data1']], + type: 'line' + }; + config.axis.x = { + type: 'timeseries', + tick: { + format: '%I:%M:%S' + } + }; + const chart = c3.generate(config); + if (this.props.tenant && this.props.type && this.props.metric) { + let typeForUrl = this.props.type; + if (typeForUrl != "availability") { + typeForUrl += "s"; + } + // FIXME: manage non numeric types + // Fetch datapoints + $.ajax({ + url: "/hawkular/metrics/" + typeForUrl + "/" + encodeURI(this.props.metric) + "/raw?order=ASC", + contentType: "application/json", + headers: {"Hawkular-Tenant": this.props.tenant}, + success: (datapoints, textStatus, xhr) => { + if (datapoints) { + chart.load({ + columns: [ + ['x'].concat(datapoints.map(dp => dp.timestamp)), + ['data1'].concat(datapoints.map(dp => dp.value)) + ] + }); + } + } + }); + } + } +} + +class Metrics extends React.Component { + constructor(props) { + super(props); + this.onTenantChanged = this.onTenantChanged.bind(this); + this.state = { + tenant: this.props.params.tenant || "", + message: "", + metrics: [] + }; + if (this.props.params && this.props.params.tenant) { + this.state.metrics = ["Loading..."]; + this.fetchMetrics(); + } + } + + render() { + console.log("Rendering Metrics"); + return ( +
+ +
+ +
+
+ ) + } + + onTenantChanged(event) { + this.setState({tenant: event.target.value}); + if (this.tenantChangeHandle) { + clearTimeout(this.tenantChangeHandle); + } + this.tenantChangeHandle = setTimeout(() => { + this.fetchMetrics(); + this.tenantChangeHandle = null; + }, 200); + } + + fetchMetrics() { + $.ajax({ + url: "/hawkular/metrics/metrics", + contentType: "application/json", + headers: {"Hawkular-Tenant": this.state.tenant}, + success: (metrics, textStatus, xhr) => { + if (metrics.length > 0) { + this.setState({metrics: metrics.sort((a,b) => { + return a.id < b.id ? -1 : 1; + }), message: ""}); + } else { + this.setState({ + message: "No metric found for this tenant" + }); + } + }, + complete: (xhr, textStatus) => { + if (xhr.status === 404 || xhr.status === 503) { + this.setState({ + message: "The server is not available" + }); + } else if (xhr.status != 200) { + this.setState({ + message: "An error occured while accessing the server: " + textStatus + }); + } + } + }); + } +} class Status extends React.Component { constructor(props) { @@ -40,59 +188,56 @@ class Status extends React.Component { render() { return (
-
- Hawkular Logo -
-

Hawkular Metrics

-

A time series metrics engine based on Cassandra

+ +
+
+ Hawkular Logo +
+

Hawkular Metrics

+

A time series metrics engine based on Cassandra

-

{this.state.version}

-

{this.state.gitref}

-

{this.state.status}

+

{this.state.version}

+

{this.state.gitref}

+

{this.state.status}

+
) } componentDidMount() { - var httpRequest; - - if (window.XMLHttpRequest) { - httpRequest = new XMLHttpRequest(); - } else if (window.ActiveXObject) { - try { - httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); - } catch (e) { - httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); - } - } - - httpRequest.onreadystatechange = () => { - if (httpRequest.readyState === 4) { - if (httpRequest.status === 200) { - const statusJson = JSON.parse(httpRequest.responseText); - this.setState({ - version: statusJson["Implementation-Version"], - gitref: "(Git SHA1 - " + statusJson["Built-From-Git-SHA1"] + ")", - status: "Metrics Service: " + statusJson.MetricsService - }); - } else if (httpRequest.status === 404 || httpRequest.status === 503) { + $.ajax({ + url: "/hawkular/metrics/status", + success: (data, textStatus, xhr) => { + const statusJson = JSON.parse(textStatus); + this.setState({ + version: statusJson["Implementation-Version"], + gitref: "(Git SHA1 - " + statusJson["Built-From-Git-SHA1"] + ")", + status: "Metrics Service: " + statusJson.MetricsService + }); + }, + complete: (xhr, textStatus) => { + if (xhr.status === 404 || xhr.status === 503) { this.setState({ status: "The server is not available" }); - } else { + } else if (xhr != 200) { this.setState({ - status: "An error occured while accessing the server :" + httpRequest.responseText + status: "An error occured while accessing the server: " + textStatus }); } } - }; - httpRequest.open("GET", "/hawkular/metrics/status"); - httpRequest.send(); + }); } } const Menu = ( -