Commit 1b47cc06 by Johannes Edmeier

Add error handling for details view

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