Commit 1b47cc06 by Johannes Edmeier

Add error handling for details view

parent cd340355
...@@ -2996,7 +2996,7 @@ ...@@ -2996,7 +2996,7 @@
"d3-dispatch": "1.0.3", "d3-dispatch": "1.0.3",
"d3-drag": "1.2.1", "d3-drag": "1.2.1",
"d3-interpolate": "1.1.6", "d3-interpolate": "1.1.6",
"d3-selection": "1.2.0", "d3-selection": "1.3.0",
"d3-transition": "1.1.1" "d3-transition": "1.1.1"
} }
}, },
...@@ -3021,7 +3021,7 @@ ...@@ -3021,7 +3021,7 @@
"integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==", "integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==",
"requires": { "requires": {
"d3-dispatch": "1.0.3", "d3-dispatch": "1.0.3",
"d3-selection": "1.2.0" "d3-selection": "1.3.0"
} }
}, },
"d3-ease": { "d3-ease": {
...@@ -3062,9 +3062,9 @@ ...@@ -3062,9 +3062,9 @@
} }
}, },
"d3-selection": { "d3-selection": {
"version": "1.2.0", "version": "1.3.0",
"resolved": "http://10.103.55.169/content/groups/npm-public/d3-selection/-/d3-selection-1.2.0.tgz", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz",
"integrity": "sha512-xW2Pfcdzh1gOaoI+LGpPsLR2VpBQxuFoxvrvguK8ZmrJbPIVvfNG6pU6GNfK41D6Qz15sj61sbW/AFYuukwaLQ==" "integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA=="
}, },
"d3-shape": { "d3-shape": {
"version": "1.2.0", "version": "1.2.0",
...@@ -3101,7 +3101,7 @@ ...@@ -3101,7 +3101,7 @@
"d3-dispatch": "1.0.3", "d3-dispatch": "1.0.3",
"d3-ease": "1.0.3", "d3-ease": "1.0.3",
"d3-interpolate": "1.1.6", "d3-interpolate": "1.1.6",
"d3-selection": "1.2.0", "d3-selection": "1.3.0",
"d3-timer": "1.0.7" "d3-timer": "1.0.7"
} }
}, },
...@@ -11406,13 +11406,25 @@ ...@@ -11406,13 +11406,25 @@
"dev": true "dev": true
}, },
"style-loader": { "style-loader": {
"version": "0.19.1", "version": "0.20.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.19.1.tgz", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.1.tgz",
"integrity": "sha512-IRE+ijgojrygQi3rsqT0U4dd+UcPCqcVvauZpCnQrGAlEe+FUIyrK93bUDScamesjP08JlQNsFJU+KmPedP5Og==", "integrity": "sha512-NtlwQOHQvUgEKuPs4JoUMQUkML8UNMxLbXM2JAZerIQVVVMgO5VVRjYQA8zzkpBu/X2OnTt+5ZKe8IbGk5TjRA==",
"dev": true, "dev": true,
"requires": { "requires": {
"loader-utils": "1.1.0", "loader-utils": "1.1.0",
"schema-utils": "0.3.0" "schema-utils": "0.4.3"
},
"dependencies": {
"schema-utils": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.3.tgz",
"integrity": "sha512-sgv/iF/T4/SewJkaVpldKC4WjSkz0JsOh2eKtxCPpCO1oR05+7MOF+H476HVRbLArkgA7j5TRJJ4p2jdFkUGQQ==",
"dev": true,
"requires": {
"ajv": "5.2.3",
"ajv-keywords": "2.1.0"
}
}
} }
}, },
"supports-color": { "supports-color": {
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
"d3-axis": "^1.0.8", "d3-axis": "^1.0.8",
"d3-brush": "^1.0.4", "d3-brush": "^1.0.4",
"d3-scale": "^1.0.7", "d3-scale": "^1.0.7",
"d3-selection": "^1.2.0", "d3-selection": "^1.3.0",
"d3-shape": "^1.2.0", "d3-shape": "^1.2.0",
"d3-time": "^1.0.8", "d3-time": "^1.0.8",
"event-source-polyfill": "0.0.12", "event-source-polyfill": "0.0.12",
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
"optimize-css-assets-webpack-plugin": "^3.2.0", "optimize-css-assets-webpack-plugin": "^3.2.0",
"postcss-loader": "^2.0.10", "postcss-loader": "^2.0.10",
"sass-loader": "^6.0.6", "sass-loader": "^6.0.6",
"style-loader": "^0.19.1", "style-loader": "^0.20.1",
"url-loader": "^0.6.2", "url-loader": "^0.6.2",
"vue-loader": "^13.7.0", "vue-loader": "^13.7.0",
"vue-svg-loader": "^0.4.0", "vue-svg-loader": "^0.4.0",
......
...@@ -47,6 +47,11 @@ class Instance { ...@@ -47,6 +47,11 @@ class Instance {
}); });
} }
async fetchMetrics() {
return axios.get(`instances/${this.id}/actuator/metrics`, {
headers: {'Accept': actuatorMimeTypes}
});
}
async fetchMetric(metric, tags) { async fetchMetric(metric, tags) {
const params = tags ? {tag: _.entries(tags).map(([name, value]) => `${name}:${value}`).join(',')} : {}; const params = tags ? {tag: _.entries(tags).map(([name, value]) => `${name}:${value}`).join(',')} : {};
return axios.get(`instances/${this.id}/actuator/metrics/${metric}`, { return axios.get(`instances/${this.id}/actuator/metrics/${metric}`, {
......
...@@ -36,14 +36,13 @@ ...@@ -36,14 +36,13 @@
import subscribing from '@/mixins/subscribing'; import subscribing from '@/mixins/subscribing';
import {Observable} from '@/utils/rxjs'; import {Observable} from '@/utils/rxjs';
import AuditeventsList from '@/views/instances/auditevents/auditevents-list'; import AuditeventsList from '@/views/instances/auditevents/auditevents-list';
import _ from 'lodash';
import moment from 'moment'; import moment from 'moment';
class Auditevent { class Auditevent {
constructor({timestamp, type, principal, data}) { constructor({timestamp, ...event}) {
this.timestamp = moment(timestamp); this.timestamp = moment(timestamp);
this.type = type; Object.assign(this, event);
this.principal = principal;
this.data = data;
} }
get key() { get key() {
...@@ -101,7 +100,7 @@ ...@@ -101,7 +100,7 @@
.subscribe({ .subscribe({
next: events => { next: events => {
vm.hasLoaded = true; vm.hasLoaded = true;
vm.events = vm.events ? events.concat(vm.events) : events; vm.addEvents(events);
}, },
error: error => { error: error => {
vm.hasLoaded = true; vm.hasLoaded = true;
...@@ -110,6 +109,9 @@ ...@@ -110,6 +109,9 @@
} }
}); });
} }
},
addEvents(events) {
this.events = _.uniqBy(this.events ? events.concat(this.events) : events, event => event.key);
} }
} }
} }
......
...@@ -15,9 +15,17 @@ ...@@ -15,9 +15,17 @@
--> -->
<template> <template>
<sba-panel :title="`Cache: ${cacheName}`" v-if="current"> <sba-panel :title="`Cache: ${cacheName}`" v-if="hasLoaded">
<div slot="text"> <div slot="text">
<div class="level cache-current"> <div v-if="error" class="message is-danger">
<div class="message-body">
<strong>
<font-awesome-icon class="has-text-danger" icon="exclamation-triangle"></font-awesome-icon>
Fetching cache metrics failed.
</strong>
</div>
</div>
<div class="level cache-current" v-if="current">
<div class="level-item has-text-centered"> <div class="level-item has-text-centered">
<div> <div>
<p class="heading has-bullet has-bullet-info">Hits</p> <p class="heading has-bullet has-bullet-info">Hits</p>
...@@ -37,7 +45,7 @@ ...@@ -37,7 +45,7 @@
</div> </div>
</div> </div>
</div> </div>
<cache-chart :data="chartData"></cache-chart> <cache-chart v-if="chartData.length > 0" :data="chartData"></cache-chart>
</div> </div>
</sba-panel> </sba-panel>
</template> </template>
...@@ -53,6 +61,8 @@ ...@@ -53,6 +61,8 @@
mixins: [subscribing], mixins: [subscribing],
components: {cacheChart}, components: {cacheChart},
data: () => ({ data: () => ({
hasLoaded: false,
error: null,
current: null, current: null,
chartData: [], chartData: [],
}), }),
...@@ -89,14 +99,16 @@ ...@@ -89,14 +99,16 @@
const vm = this; const vm = this;
if (this.instance) { if (this.instance) {
return Observable.timer(0, 2500) return Observable.timer(0, 2500)
.concatMap(this.fetchMetrics) .concatMap(vm.fetchMetrics)
.subscribe({ .subscribe({
next: data => { next: data => {
vm.current = data; vm.current = data;
vm.chartData.push({...data, timestamp: moment.now().valueOf()}); vm.chartData.push({...data, timestamp: moment.now().valueOf()});
}, },
error: err => { error: error => {
vm.unsubscribe(); vm.hasLoaded = true;
console.warn(`Fetching cache ${vm.cacheName} metrics failed:`, error);
vm.error = error;
} }
}); });
} }
......
...@@ -42,12 +42,8 @@ ...@@ -42,12 +42,8 @@
methods: { methods: {
async fetchcaches() { async fetchcaches() {
if (this.instance) { if (this.instance) {
try { const response = await this.instance.fetchMetric('cache.requests');
const response = await this.instance.fetchMetric('cache.requests'); return _.uniq(response.data.availableTags.filter(tag => tag.tag === 'name')[0].values);
return _.uniq(response.data.availableTags.filter(tag => tag.tag === 'name')[0].values);
} catch (error) {
return [];
}
} }
}, },
createSubscription() { createSubscription() {
...@@ -59,8 +55,8 @@ ...@@ -59,8 +55,8 @@
next: names => { next: names => {
vm.caches = names vm.caches = names
}, },
error: err => { error: error => {
vm.unsubscribe(); console.warn('Fetching caches failed:', error);
} }
}); });
} }
......
...@@ -15,9 +15,17 @@ ...@@ -15,9 +15,17 @@
--> -->
<template> <template>
<sba-panel :title="`Datasource: ${dataSource}`" v-if="current"> <sba-panel :title="`Datasource: ${dataSource}`" v-if="hasLoaded">
<div slot="text"> <div slot="text">
<div class="level datasource-current"> <div v-if="error" class="message is-danger">
<div class="message-body">
<strong>
<font-awesome-icon class="has-text-danger" icon="exclamation-triangle"></font-awesome-icon>
Fetching datasource metrics failed.
</strong>
</div>
</div>
<div class="level datasource-current" v-if="current">
<div class="level-item has-text-centered"> <div class="level-item has-text-centered">
<div> <div>
<p class="heading has-bullet has-bullet-info">Active connections</p> <p class="heading has-bullet has-bullet-info">Active connections</p>
...@@ -38,7 +46,7 @@ ...@@ -38,7 +46,7 @@
</div> </div>
</div> </div>
</div> </div>
<datasource-chart :data="chartData"></datasource-chart> <datasource-chart v-if="chartData.length > 0" :data="chartData"></datasource-chart>
</div> </div>
</sba-panel> </sba-panel>
</template> </template>
...@@ -54,6 +62,8 @@ ...@@ -54,6 +62,8 @@
mixins: [subscribing], mixins: [subscribing],
components: {datasourceChart}, components: {datasourceChart},
data: () => ({ data: () => ({
hasLoaded: false,
error: null,
current: null, current: null,
chartData: [], chartData: [],
}), }),
...@@ -82,14 +92,17 @@ ...@@ -82,14 +92,17 @@
const vm = this; const vm = this;
if (this.instance) { if (this.instance) {
return Observable.timer(0, 2500) return Observable.timer(0, 2500)
.concatMap(this.fetchMetrics) .concatMap(vm.fetchMetrics)
.subscribe({ .subscribe({
next: data => { next: data => {
vm.hasLoaded = true;
vm.current = data; vm.current = data;
vm.chartData.push({...data, timestamp: moment.now().valueOf()}); vm.chartData.push({...data, timestamp: moment.now().valueOf()});
}, },
error: err => { error: error => {
vm.unsubscribe(); vm.hasLoaded = true;
console.warn(`Fetching datasource ${vm.dataSource} metrics failed:`, error);
vm.error = error;
} }
}); });
} }
......
...@@ -41,12 +41,8 @@ ...@@ -41,12 +41,8 @@
methods: { methods: {
async fetchDataSources() { async fetchDataSources() {
if (this.instance) { if (this.instance) {
try { const response = await this.instance.fetchMetric('data.source.active.connections');
const response = await this.instance.fetchMetric('data.source.active.connections'); return response.data.availableTags.filter(tag => tag.tag === 'name')[0].values;
return response.data.availableTags.filter(tag => tag.tag === 'name')[0].values;
} catch (error) {
return [];
}
} }
}, },
createSubscription() { createSubscription() {
...@@ -58,8 +54,8 @@ ...@@ -58,8 +54,8 @@
next: names => { next: names => {
vm.dataSources = names vm.dataSources = names
}, },
error: err => { error: error => {
vm.unsubscribe(); console.warn('Fetching datasources failed:', error);
} }
}); });
} }
......
...@@ -15,9 +15,17 @@ ...@@ -15,9 +15,17 @@
--> -->
<template> <template>
<sba-panel title="Garbage Collection Pauses" v-if="current"> <sba-panel title="Garbage Collection Pauses" v-if="hasLoaded">
<div slot="text"> <div slot="text">
<div class="level"> <div v-if="error" class="message is-danger">
<div class="message-body">
<strong>
<font-awesome-icon class="has-text-danger" icon="exclamation-triangle"></font-awesome-icon>
Fetching GC metrics failed.
</strong>
</div>
</div>
<div class="level" v-if="current">
<div class="level-item has-text-centered"> <div class="level-item has-text-centered">
<div> <div>
<p class="heading">Count</p> <p class="heading">Count</p>
...@@ -45,6 +53,8 @@ ...@@ -45,6 +53,8 @@
props: ['instance'], props: ['instance'],
mixins: [subscribing], mixins: [subscribing],
data: () => ({ data: () => ({
hasLoaded: false,
error: null,
current: null, current: null,
}), }),
watch: { watch: {
...@@ -73,10 +83,13 @@ ...@@ -73,10 +83,13 @@
.concatMap(this.fetchMetrics) .concatMap(this.fetchMetrics)
.subscribe({ .subscribe({
next: data => { next: data => {
vm.hasLoaded = true;
vm.current = data; vm.current = data;
}, },
error: err => { error: error => {
vm.unsubscribe(); vm.hasLoaded = true;
console.warn('Fetching GC metrics failed:', error);
vm.error = error;
} }
}); });
} }
......
...@@ -16,8 +16,18 @@ ...@@ -16,8 +16,18 @@
<template> <template>
<sba-panel title="Health"> <sba-panel title="Health">
<div class="content" slot="text"> <div slot="text">
<health-default name="Instance" :health="health"></health-default> <div v-if="error" class="message is-danger">
<div class="message-body">
<strong>
<font-awesome-icon class="has-text-danger" icon="exclamation-triangle"></font-awesome-icon>
Fetching health failed.
</strong>
</div>
</div>
<div class="content" :class="{ 'is-loading' : !hasLoaded }">
<health-default v-if="health" name="Instance" :health="health"></health-default>
</div>
</div> </div>
</sba-panel> </sba-panel>
</template> </template>
...@@ -29,7 +39,9 @@ ...@@ -29,7 +39,9 @@
components: {healthDefault}, components: {healthDefault},
props: ['instance'], props: ['instance'],
data: () => ({ data: () => ({
health: {status: 'UNKNOWN', details: []}, hasLoaded: false,
error: null,
health: null,
}), }),
created() { created() {
this.fetchHealth(); this.fetchHealth();
...@@ -42,13 +54,17 @@ ...@@ -42,13 +54,17 @@
methods: { methods: {
async fetchHealth() { async fetchHealth() {
if (this.instance) { if (this.instance) {
const res = await this.instance.fetchHealth(); this.error = null;
this.health = res.data; try {
const res = await this.instance.fetchHealth();
this.health = res.data;
} catch (error) {
console.warn('Fetching health failed:', error);
this.error = error;
}
this.hasLoaded = true;
} }
} }
} }
} }
</script> </script>
\ No newline at end of file
<style lang="scss">
</style>
\ No newline at end of file
...@@ -15,27 +15,44 @@ ...@@ -15,27 +15,44 @@
--> -->
<template> <template>
<sba-panel title="Info" v-if="info"> <sba-panel title="Info" v-if="hasLoaded">
<div class="content" slot="text"> <div slot="text">
<table class="table"> <div v-if="error" class="message is-danger">
<tr v-for="(value, key) in info" :key="key"> <div class="message-body">
<td class="info__key" v-text="key"></td> <strong>
<td> <font-awesome-icon class="has-text-danger" icon="exclamation-triangle"></font-awesome-icon>
<sba-formatted-obj :value="value"></sba-formatted-obj> Fetching info failed.
</td> </strong>
</tr> </div>
</table> </div>
<div class="content" v-if="info">
<table class="table" v-if="!isEmptyInfo">
<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>
<p v-else class="is-muted">No info provided.</p>
</div>
</div> </div>
</sba-panel> </sba-panel>
</template> </template>
<script> <script>
export default { export default {
props: ['instance'], props: ['instance'],
data: () => ({ data: () => ({
info: null, hasLoaded: false,
error: null,
info: null
}), }),
computed: {
isEmptyInfo() {
return Object.keys(this.info).length <= 0;
}
},
created() { created() {
this.fetchInfo(); this.fetchInfo();
}, },
...@@ -46,11 +63,16 @@ ...@@ -46,11 +63,16 @@
}, },
methods: { methods: {
async fetchInfo() { async fetchInfo() {
if (this.instance) { if (this.instance && this.instance.hasEndpoint('info')) {
const res = await this.instance.fetchInfo(); try {
if (Object.keys(res.data).length > 0) { this.error = null;
const res = await this.instance.fetchInfo();
this.info = res.data; this.info = res.data;
} catch (error) {
this.error = error;
console.warn('Fetching info failed:', error);
} }
this.hasLoaded = true;
} }
} }
} }
......
...@@ -15,9 +15,17 @@ ...@@ -15,9 +15,17 @@
--> -->
<template> <template>
<sba-panel :title="`Memory: ${name}`" v-if="current"> <sba-panel :title="`Memory: ${name}`" v-if="hasLoaded">
<div slot="text"> <div slot="text">
<div class="level memory-current"> <div v-if="error" class="message is-danger">
<div class="message-body">
<strong>
<font-awesome-icon class="has-text-danger" icon="exclamation-triangle"></font-awesome-icon>
Fetching memory metrics failed.
</strong>
</div>
</div>
<div class="level memory-current" v-if="current">
<div class="level-item has-text-centered" v-if="current.metaspace"> <div class="level-item has-text-centered" v-if="current.metaspace">
<div> <div>
<p class="heading has-bullet has-bullet-primary">Metaspace</p> <p class="heading has-bullet has-bullet-primary">Metaspace</p>
...@@ -43,7 +51,7 @@ ...@@ -43,7 +51,7 @@
</div> </div>
</div> </div>
</div> </div>
<mem-chart :data="chartData"></mem-chart> <mem-chart v-if="chartData.length > 0" :data="chartData"></mem-chart>
</div> </div>
</sba-panel> </sba-panel>
</template> </template>
...@@ -60,6 +68,8 @@ ...@@ -60,6 +68,8 @@
mixins: [subscribing], mixins: [subscribing],
components: {memChart}, components: {memChart},
data: () => ({ data: () => ({
hasLoaded: false,
error: null,
current: null, current: null,
chartData: [], chartData: [],
}), }),
...@@ -105,11 +115,14 @@ ...@@ -105,11 +115,14 @@
.concatMap(this.fetchMetrics) .concatMap(this.fetchMetrics)
.subscribe({ .subscribe({
next: data => { next: data => {
vm.hasLoaded = true;
vm.current = data; vm.current = data;
vm.chartData.push({...data, timestamp: moment.now().valueOf()}); vm.chartData.push({...data, timestamp: moment.now().valueOf()});
}, },
error: err => { error: error => {
vm.unsubscribe(); vm.hasLoaded = true;
console.warn('Fetching memory metrics failed:', error);
vm.error = error;
} }
}); });
} }
......
...@@ -15,32 +15,42 @@ ...@@ -15,32 +15,42 @@
--> -->
<template> <template>
<sba-panel title="Process" v-if="pid || metrics['process.uptime'] "> <sba-panel title="Process" v-if="hasLoaded">
<div class="level" slot="text"> <div slot="text">
<div class="level-item has-text-centered" v-if="pid"> <div v-if="error" class="message is-danger">
<div> <div class="message-body">
<p class="heading">PID</p> <strong>
<p v-text="pid"></p> <font-awesome-icon class="has-text-danger" icon="exclamation-triangle"></font-awesome-icon>
Fetching process metrics failed.
</strong>
</div> </div>
</div> </div>
<div class="level-item has-text-centered" v-if="metrics['process.uptime']"> <div class="level">
<div> <div class="level-item has-text-centered" v-if="pid">
<p class="heading">Uptime</p> <div>
<p> <p class="heading">PID</p>
<process-uptime :value="metrics['process.uptime']"></process-uptime> <p v-text="pid"></p>
</p> </div>
</div> </div>
</div> <div class="level-item has-text-centered" v-if="metrics['process.uptime']">
<div class="level-item has-text-centered" v-if="metrics['system.cpu.count']"> <div>
<div> <p class="heading">Uptime</p>
<p class="heading">CPUs</p> <p>
<p v-text="metrics['system.cpu.count']"></p> <process-uptime :value="metrics['process.uptime']"></process-uptime>
</p>
</div>
</div> </div>
</div> <div class="level-item has-text-centered" v-if="metrics['system.cpu.count']">
<div class="level-item has-text-centered" v-if="metrics['system.load.average.1m']"> <div>
<div> <p class="heading">CPUs</p>
<p class="heading">System Load (last 1m)</p> <p v-text="metrics['system.cpu.count']"></p>
<p v-text="metrics['system.load.average.1m'].toFixed(2)"></p> </div>
</div>
<div class="level-item has-text-centered" v-if="metrics['system.load.average.1m']">
<div>
<p class="heading">System Load (last 1m)</p>
<p v-text="metrics['system.load.average.1m'].toFixed(2)"></p>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -55,6 +65,8 @@ ...@@ -55,6 +65,8 @@
props: ['instance'], props: ['instance'],
components: {processUptime}, components: {processUptime},
data: () => ({ data: () => ({
hasLoaded: false,
error: null,
pid: null, pid: null,
metrics: { metrics: {
'process.uptime': null, 'process.uptime': null,
...@@ -76,18 +88,29 @@ ...@@ -76,18 +88,29 @@
async fetchMetrics() { async fetchMetrics() {
if (this.instance) { if (this.instance) {
const vm = this; const vm = this;
_.entries(vm.metrics).forEach(async ([name]) => { vm.error = null;
const response = await vm.instance.fetchMetric(name); try {
vm.metrics[name] = response.data.measurements[0].value; await Promise.all(_.entries(vm.metrics)
} .map(async ([name]) => {
) const response = await vm.instance.fetchMetric(name);
vm.metrics[name] = response.data.measurements[0].value;
}
));
} catch (error) {
console.warn('Fetching process metrics failed:', error);
this.error = error;
}
this.hasLoaded = true;
} }
}, },
async fetchPid() { async fetchPid() {
if (this.instance) { if (this.instance && this.instance.hasEndpoint('env')) {
const vm = this; try {
const response = await vm.instance.fetchEnv('PID'); const response = await this.instance.fetchEnv('PID');
vm.pid = response.data.property.value; this.pid = response.data.property.value;
} catch (error) {
console.warn('Fetching PID failed:', error);
}
} }
} }
} }
......
...@@ -15,9 +15,17 @@ ...@@ -15,9 +15,17 @@
--> -->
<template> <template>
<sba-panel title="Threads" v-if="current"> <sba-panel title="Threads" v-if="hasLoaded">
<div slot="text"> <div slot="text">
<div class="level threads-current"> <div v-if="error" class="message is-danger">
<div class="message-body">
<strong>
<font-awesome-icon class="has-text-danger" icon="exclamation-triangle"></font-awesome-icon>
Fetching threads metrics failed.
</strong>
</div>
</div>
<div class="level threads-current" v-if="current">
<div class="level-item has-text-centered"> <div class="level-item has-text-centered">
<div> <div>
<p class="heading has-bullet has-bullet-warning">Live</p> <p class="heading has-bullet has-bullet-warning">Live</p>
...@@ -37,7 +45,7 @@ ...@@ -37,7 +45,7 @@
</div> </div>
</div> </div>
</div> </div>
<threads-chart :data="chartData"></threads-chart> <threads-chart v-if="chartData.length > 0" :data="chartData"></threads-chart>
</div> </div>
</sba-panel> </sba-panel>
</template> </template>
...@@ -53,6 +61,8 @@ ...@@ -53,6 +61,8 @@
mixins: [subscribing], mixins: [subscribing],
components: {threadsChart}, components: {threadsChart},
data: () => ({ data: () => ({
hasLoaded: false,
error: null,
current: null, current: null,
chartData: [], chartData: [],
}), }),
...@@ -84,11 +94,14 @@ ...@@ -84,11 +94,14 @@
.concatMap(this.fetchMetrics) .concatMap(this.fetchMetrics)
.subscribe({ .subscribe({
next: data => { next: data => {
vm.hasLoaded = true;
vm.current = data; vm.current = data;
vm.chartData.push({...data, timestamp: moment.now().valueOf()}); vm.chartData.push({...data, timestamp: moment.now().valueOf()});
}, },
error: err => { error: error => {
vm.unsubscribe(); vm.hasLoaded = true;
console.warn('Fetching threads metrics failed:', error);
vm.error = error;
} }
}); });
} }
......
...@@ -17,9 +17,17 @@ ...@@ -17,9 +17,17 @@
<template> <template>
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<div v-if="error" class="message is-danger">
<div class="message-body">
<strong>
<font-awesome-icon class="has-text-danger" icon="exclamation-triangle"></font-awesome-icon>
Fetching metrics index failed.
</strong>
</div>
</div>
<div class="columns is-desktop"> <div class="columns is-desktop">
<div class="column"> <div class="column">
<details-info :instance="instance"></details-info> <details-info v-if="hasInfo" :instance="instance"></details-info>
</div> </div>
<div class="column"> <div class="column">
<details-health :instance="instance"></details-health> <details-health :instance="instance"></details-health>
...@@ -27,27 +35,27 @@ ...@@ -27,27 +35,27 @@
</div> </div>
<div class="columns is-desktop"> <div class="columns is-desktop">
<div class="column"> <div class="column">
<details-process :instance="instance"></details-process> <details-process v-if="hasProcess" :instance="instance"></details-process>
<details-gc :instance="instance"></details-gc> <details-gc v-if="hasGc" :instance="instance"></details-gc>
</div> </div>
<div class="column"> <div class="column">
<details-threads :instance="instance"></details-threads> <details-threads v-if="hasThreads" :instance="instance"></details-threads>
</div> </div>
</div> </div>
<div class="columns is-desktop"> <div class="columns is-desktop">
<div class="column"> <div class="column">
<details-memory :instance="instance" type="heap"></details-memory> <details-memory v-if="hasMemory" :instance="instance" type="heap"></details-memory>
</div> </div>
<div class="column"> <div class="column">
<details-memory :instance="instance" type="nonheap"></details-memory> <details-memory v-if="hasMemory" :instance="instance" type="nonheap"></details-memory>
</div> </div>
</div> </div>
<div class="columns is-desktop"> <div class="columns is-desktop">
<div class="column"> <div class="column">
<details-datasources :instance="instance"></details-datasources> <details-datasources v-if="hasDatasources" :instance="instance"></details-datasources>
</div> </div>
<div class="column"> <div class="column">
<details-caches :instance="instance"></details-caches> <details-caches v-if="hasCaches" :instance="instance"></details-caches>
</div> </div>
</div> </div>
</div> </div>
...@@ -75,6 +83,57 @@ ...@@ -75,6 +83,57 @@
detailsGc, detailsGc,
detailsCaches detailsCaches
}, },
props: ['instance'] props: ['instance'],
data: () => ({
hasLoaded: false,
error: null,
metrics: []
}),
computed: {
hasCaches() {
return this.metrics.indexOf('cache.requests') >= 0;
},
hasDatasources() {
return this.metrics.indexOf('data.source.active.connections') >= 0;
},
hasGc() {
return this.metrics.indexOf('jvm.gc.pause') >= 0;
},
hasInfo() {
return this.instance && this.instance.hasEndpoint('info');
},
hasMemory() {
return this.metrics.indexOf('jvm.memory.max') >= 0;
},
hasProcess() {
return this.metrics.indexOf('process.uptime') >= 0;
},
hasThreads() {
return this.metrics.indexOf('jvm.threads.live') >= 0;
},
},
created() {
this.fetchMetricIndex();
},
watch: {
instance() {
this.fetchMetricIndex();
}
},
methods: {
async fetchMetricIndex() {
if (this.instance && this.instance.hasEndpoint('metrics')) {
this.error = null;
try {
const res = await this.instance.fetchMetrics();
this.metrics = res.data.names;
} catch (error) {
console.warn('Fetching metric index failed:', error);
this.error = error;
}
this.hasLoaded = true;
}
}
}
} }
</script> </script>
\ No newline at end of file
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
--> -->
<template> <template>
<div class="section logfile-view" :class="{ 'is-loading' : hasLoaded }"> <div class="section logfile-view" :class="{ 'is-loading' : !hasLoaded }">
<div v-if="error" class="message is-danger"> <div v-if="error" class="message is-danger">
<div class="message-body"> <div class="message-body">
<strong> <strong>
......
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