Commit b3137edd by Johannes Edmeier

Add details for jvm and datasources

parent 01c0d1b4
......@@ -29,11 +29,6 @@
cursor: pointer;
}
.card .table tr:last-of-type > td,
.card .table tr:last-of-type > th {
border-bottom: none;
}
.section.is-loading {
&::after {
@include loader;
......@@ -43,4 +38,15 @@
top: calc(50% - 2.5em);
border-width: 0.5rem;
}
}
\ No newline at end of file
}
.data-table {
& td:last-child {
width: 100%;
text-align: right;
}
& td:not(:last-child) {
white-space: nowrap;
}
}
......@@ -48,6 +48,14 @@ class Instance {
});
}
async fetchMetric(metric, tags) {
const params = tags ? {tags: _.entries(tags).map(([name, value]) => `${name}:${value}`)} : {};
return axios.get(`instances/${this.id}/actuator/metrics/${metric}`, {
headers: {'Accept': actuatorMimeTypes},
params
});
}
async fetchHealth() {
return axios.get(`instances/${this.id}/actuator/health`, {
headers: {'Accept': actuatorMimeTypes}
......
......@@ -26,10 +26,10 @@
<p>
To monitor applications, they must be registered at this server. This is either done by including
the <a
href="http://codecentric.github.io/spring-boot-admin/@project.version@/#register-clients-via-spring-boot-admin">Spring
href="https://codecentric.github.io/spring-boot-admin/@project.version@/#register-clients-via-spring-boot-admin">Spring
Boot Admin Client</a>
or using a <a
href="http://codecentric.github.io/spring-boot-admin/@project.version@/#discover-clients-via-spring-cloud-discovery">Spring
href="https://codecentric.github.io/spring-boot-admin/@project.version@/#discover-clients-via-spring-cloud-discovery">Spring
Cloud Discovery Client</a> implementation.
</p>
<p>
......
<!--
- Copyright 2014-2018 the original author or authors.
-
- 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.
-->
<template>
<sba-panel title="DataSources" v-if="hasDataSources">
<div class="content" slot="text">
<table class="table data-table">
<template v-for="dataSource in dataSources">
<tr>
<td :rowspan="3" v-text="dataSource.name"></td>
<td colspan="2">
<span class="is-pulled-left">active connections</span>
<span v-text="dataSource.active"></span>
<progress v-if="dataSource.max >= 0"
class="progress is-small" :value="dataSource.active"
:max="dataSource.max"></progress>
</td>
</tr>
<tr>
<td>min connections</td>
<td class="has-text-right" v-text="dataSource.min"></td>
</tr>
<tr>
<td>max connections</td>
<td class="has-text-right" v-text="dataSource.max" v-if="dataSource.max >= 0"></td>
<td class="has-text-right" v-else>unlimited</td>
</tr>
</template>
</table>
</div>
</sba-panel>
</template>
<script>
export default {
props: ['instance'],
data: () => ({
hasDataSources: false,
dataSources: [],
}),
created() {
this.fetchMetrics();
},
watch: {
instance(newVal, oldVal) {
if (newVal !== oldVal) {
this.fetchMetrics();
}
}
},
methods: {
async fetchMetrics() {
if (this.instance) {
try {
const response = await this.instance.fetchMetric('data.source.active.connections');
this.hasDataSources = true;
const dataSourcesNames = response.data.availableTags.filter(tag => tag.tag === 'name')[0].values;
dataSourcesNames.forEach(this.fetchDataSourceMetrics);
} catch (error) {
this.hasDataSources = false;
}
}
},
async fetchDataSourceMetrics(name) {
const responseActive = this.instance.fetchMetric('data.source.active.connections', {name});
const responseMin = this.instance.fetchMetric('data.source.min.connections', {name});
const responseMax = this.instance.fetchMetric('data.source.max.connections', {name});
this.dataSources.push({
name,
active: (await responseActive).data.measurements[0].value,
min: (await responseMin).data.measurements[0].value,
max: (await responseMax).data.measurements[0].value
});
}
}
}
</script>
\ No newline at end of file
......@@ -15,7 +15,11 @@
-->
<template>
<health-default name="Instance" :health="health"></health-default>
<sba-panel title="Health">
<div class="content" slot="text">
<health-default name="Instance" :health="health"></health-default>
</div>
</sba-panel>
</template>
<script>
......
......@@ -15,14 +15,18 @@
-->
<template>
<table class="table">
<tr v-for="(value, key) in info" :key="key">
<td class="info__key" v-text="key"></td>
<td>
<sba-formatted-obj :value="value"></sba-formatted-obj>
</td>
</tr>
</table>
<sba-panel title="Info" v-if="info">
<div class="content" slot="text">
<table class="table">
<tr v-for="(value, key) in info" :key="key">
<td class="info__key" v-text="key"></td>
<td>
<sba-formatted-obj :value="value"></sba-formatted-obj>
</td>
</tr>
</table>
</div>
</sba-panel>
</template>
<script>
......@@ -46,7 +50,9 @@
async fetchInfo() {
if (this.instance) {
const res = await this.instance.fetchInfo();
this.info = res.data;
if (Object.keys(res.data).length > 0) {
this.info = res.data;
}
}
}
}
......
<!--
- Copyright 2014-2018 the original author or authors.
-
- 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.
-->
<template>
<sba-panel title="JVM" v-if="metrics['process.uptime']">
<div class="content" slot="text">
<table class="table data-table">
<tr>
<td>Uptime</td>
<td colspan="2">
<jvm-uptime :value="metrics['process.uptime']"></jvm-uptime>
</td>
</tr>
<tr v-if="metrics['system.load.average.1m']">
<td>System load (last 1m)</td>
<td colspan="2"
v-text="metrics['system.load.average.1m'].toFixed(2)"></td>
</tr>
<tr>
<td>Available CPUs</td>
<td colspan="2" v-text="metrics['system.cpu.count']"></td>
</tr>
<tr>
<td rowspan="3">Threads</td>
<td>live</td>
<td v-text="metrics['jvm.threads.live']"></td>
</tr>
<tr>
<td>peak</td>
<td v-text="metrics['jvm.threads.peak']"></td>
</tr>
<tr>
<td>daemon</td>
<td v-text="metrics['jvm.threads.daemon']"></td>
</tr>
</table>
</div>
</sba-panel>
</template>
<script>
import _ from 'lodash';
import jvmUptime from './jvm-uptime'
export default {
props: ['instance'],
components: {
jvmUptime
},
data: () => ({
metrics: {
'process.uptime': null,
'system.load.average.1m': null,
'system.cpu.count': null,
'jvm.threads.live': null,
'jvm.threads.peak': null,
'jvm.threads.daemon': null
}
}),
created() {
this.fetchMetrics();
},
watch: {
instance(newVal, oldVal) {
if (newVal !== oldVal) {
this.fetchMetrics();
}
}
},
methods: {
async fetchMetrics() {
if (this.instance) {
const vm = this;
_.entries(vm.metrics).forEach(async ([name]) => {
const response = await vm.instance.fetchMetric(name);
vm.metrics[name] = response.data.measurements[0].value;
}
)
}
}
}
}
</script>
\ No newline at end of file
......@@ -15,7 +15,7 @@
-->
<template>
<table class="table is-fullwidth">
<table class="table">
<tr>
<td>
<sba-status v-if="health.status" :status="health.status"></sba-status>
......@@ -24,7 +24,7 @@
</tr>
<tr v-if="details && details.length > 0">
<td>
<table class="table is-fullwidth">
<table class="table ">
<tr v-for="detail in details" :key="detail.name">
<td v-text="detail.name"></td>
<td v-text="detail.value"></td>
......
......@@ -19,14 +19,12 @@
<div class="container">
<div class="columns">
<div class="column">
<sba-panel title="Info">
<details-info slot="text" :instance="instance"></details-info>
</sba-panel>
<details-info :instance="instance"></details-info>
<details-jvm :instance="instance"></details-jvm>
</div>
<div class="column">
<sba-panel title="Health">
<details-health slot="text" :instance="instance"></details-health>
</sba-panel>
<details-health :instance="instance"></details-health>
<details-datasource :instance="instance"></details-datasource>
</div>
</div>
</div>
......@@ -34,12 +32,14 @@
</template>
<script>
import detailsHealth from './details-health.vue'
import detailsInfo from './details-info.vue'
import detailsDatasource from './details-datasource';
import detailsHealth from './details-health.vue';
import detailsInfo from './details-info.vue';
import detailsJvm from './details-jvm.vue';
export default {
components: {
detailsHealth, detailsInfo
detailsHealth, detailsInfo, detailsJvm, detailsDatasource
},
props: ['instance']
}
......
/*
* Copyright 2014-2018 the original author or authors.
*
* 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.
*/
import {Observable} from '@/utils/rxjs'
import moment from 'moment';
export default {
props: ['value'],
data: () => ({
startTs: null,
offset: null
}),
render() {
return this._v(this.clock);
},
computed: {
clock() {
if (!this.value) {
return null;
}
const duration = moment.duration(this.value * 1000 + this.offset);
return `${Math.floor(duration.asDays())}d ${duration.hours()}h ${duration.minutes()}m ${duration.seconds()}s`;
}
},
watch: {
value(newVal) {
this.stop();
if (newVal) {
this.start();
}
}
},
mounted() {
if (this.value && this.subscription == null) {
this.start();
}
},
beforeDestroy() {
this.stop();
},
methods: {
start() {
const vm = this;
this.startTs = moment.now();
this.offset = 0;
this.subscription = Observable.timer(0, 1000).subscribe({
next: () => {
vm.offset = moment.now().valueOf() - this.startTs.valueOf();
}
})
},
stop() {
this.startTs = null;
this.offset = null;
if (this.subscription) {
try {
this.subscription.unsubscribe();
} finally {
this.subscription = null;
}
}
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment