mirror of https://github.com/statping/statping
service view page updates, chart updates
parent
9ebd8e169f
commit
c068401690
|
@ -6,6 +6,7 @@
|
|||
- Added "reason" for failures (will be used for more custom notification messages) [regex, lookup, timeout, connection, close, status_code]
|
||||
- Added Help page that is generated from Statping's Wiki repo on build
|
||||
- Modified Service Group failures on index page to show 90 days of failures
|
||||
- Modified Service view page, updated Latency and Ping charts, added failures below
|
||||
|
||||
# 0.90.63 (08-17-2020)
|
||||
- Modified build process to use xgo for all arch builds
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 no-select">
|
||||
<p class="divided">
|
||||
<span class="font-2 text-muted">90 Days Ago</span>
|
||||
<span class="divider"></span>
|
||||
<span class="text-center font-2" :class="{'text-muted': service.online, 'text-danger': !service.online}">{{service_txt}}</span>
|
||||
<span class="divider"></span>
|
||||
<span class="font-2 text-muted">Today</span>
|
||||
</p>
|
||||
<p class="divided">
|
||||
<span class="font-2 text-muted">90 Days Ago</span>
|
||||
<span class="divider"></span>
|
||||
<span class="text-center font-2" :class="{'text-muted': service.online, 'text-danger': !service.online}">{{service_txt}}</span>
|
||||
<span class="divider"></span>
|
||||
<span class="font-2 text-muted">Today</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="daily-failures small text-right text-dim">{{hover_text}}</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="service-chart-container">
|
||||
<apexchart width="100%" type="area" :options="main_chart_options" :series="main_chart"></apexchart>
|
||||
<apexchart width="100%" height="350" type="area" :options="main_chart_options" :series="main_chart"></apexchart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
|||
return {
|
||||
loading: true,
|
||||
main_data: null,
|
||||
ping_data: null,
|
||||
expanded_data: null,
|
||||
main_chart_options: {
|
||||
noData: {
|
||||
|
@ -51,6 +52,7 @@
|
|||
},
|
||||
chart: {
|
||||
id: 'mainchart',
|
||||
stacked: true,
|
||||
events: {
|
||||
dataPointSelection: (event, chartContext, config) => {
|
||||
window.console.log('slect')
|
||||
|
@ -110,7 +112,9 @@
|
|||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
show: true
|
||||
formatter: (value) => {
|
||||
return this.humanTime(value)
|
||||
}
|
||||
},
|
||||
},
|
||||
markers: {
|
||||
|
@ -165,15 +169,15 @@
|
|||
show: false
|
||||
},
|
||||
fill: {
|
||||
colors: ["#48d338"],
|
||||
colors: ["#f1771f", "#48d338"],
|
||||
opacity: 1,
|
||||
type: 'solid'
|
||||
},
|
||||
stroke: {
|
||||
show: true,
|
||||
curve: 'smooth',
|
||||
curve: 'stepline',
|
||||
lineCap: 'butt',
|
||||
colors: ["#3aa82d"],
|
||||
colors: ["#f1771f", "#48d338"],
|
||||
width: 2,
|
||||
}
|
||||
},
|
||||
|
@ -227,8 +231,11 @@
|
|||
computed: {
|
||||
main_chart () {
|
||||
return [{
|
||||
name: this.service.name,
|
||||
name: "latency",
|
||||
...this.convertToChartData(this.main_data)
|
||||
},{
|
||||
name: "ping",
|
||||
...this.convertToChartData(this.ping_data)
|
||||
}]
|
||||
},
|
||||
expanded_chart () {
|
||||
|
@ -261,9 +268,13 @@
|
|||
},
|
||||
async chartHits() {
|
||||
this.main_data = await this.load_hits()
|
||||
this.ping_data = await this.load_ping()
|
||||
},
|
||||
async load_hits(start=this.params.start, end=this.params.end, group=this.group) {
|
||||
return await Api.service_hits(this.service.id, start, end, group, false)
|
||||
},
|
||||
async load_ping(start=this.params.start, end=this.params.end, group=this.group) {
|
||||
return await Api.service_ping(this.service.id, start, end, group, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<div class="text-center" style="width:210px" v-if="!loaded">
|
||||
<font-awesome-icon icon="circle-notch" class="h-25 text-dim" spin/>
|
||||
</div>
|
||||
<apexchart v-else width="100%" height="50" type="bar" :options="chartOpts" :series="data"></apexchart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "@/API";
|
||||
const timeoptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||
|
||||
|
||||
export default {
|
||||
name: "FailuresBarChart",
|
||||
props: {
|
||||
service: {
|
||||
required: true,
|
||||
type: Object,
|
||||
},
|
||||
group: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
start: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
end: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: null,
|
||||
loaded: false,
|
||||
chartOpts: {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 150,
|
||||
sparkline: {
|
||||
enabled: true
|
||||
},
|
||||
animations: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
},
|
||||
showPoint: false,
|
||||
fullWidth:true,
|
||||
chartPadding: {top: 0,right: 0,bottom: 0,left: 80},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
fill: {
|
||||
opacity: 0.4,
|
||||
},
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
colors: {
|
||||
ranges: [{
|
||||
from: 0,
|
||||
to: 1,
|
||||
color: '#cfcfcf'
|
||||
}, {
|
||||
from: 2,
|
||||
to: 3,
|
||||
color: '#f58e49'
|
||||
}, {
|
||||
from: 3,
|
||||
to: 20,
|
||||
color: '#e01a1a'
|
||||
}, {
|
||||
from: 21,
|
||||
to: Infinity,
|
||||
color: '#9b0909'
|
||||
}]
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: false,
|
||||
enabled: true,
|
||||
custom: ({series, seriesIndex, dataPointIndex, w}) => {
|
||||
let val = series[seriesIndex][dataPointIndex];
|
||||
let ts = w.globals.seriesX[seriesIndex][dataPointIndex];
|
||||
const dt = new Date(ts).toLocaleDateString("en-us", timeoptions)
|
||||
let ago = `${(dataPointIndex-12) * -1} hours ago`
|
||||
if ((dataPointIndex-12) * -1 === 0) {
|
||||
ago = `Current hour`
|
||||
}
|
||||
return `<div class="chart_list_tooltip font-2 mb-4">${val-1} Failures<br>${dt}</div>`
|
||||
},
|
||||
fixed: {
|
||||
enabled: true,
|
||||
position: 'topLeft',
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
},
|
||||
x: {
|
||||
formatter: (value) => { return value },
|
||||
},
|
||||
y: {
|
||||
show: false
|
||||
},
|
||||
},
|
||||
title: {
|
||||
enabled: false,
|
||||
},
|
||||
subtitle: {
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadFailures()
|
||||
},
|
||||
watch: {
|
||||
group(o, n) {
|
||||
this.loaded = false
|
||||
this.loadFailures()
|
||||
this.loaded = true
|
||||
},
|
||||
start(o, n) {
|
||||
this.loaded = false
|
||||
this.loadFailures()
|
||||
this.loaded = true
|
||||
},
|
||||
end(o, n) {
|
||||
this.loaded = false
|
||||
this.loadFailures()
|
||||
this.loaded = true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
convertChartData(data) {
|
||||
if (!data) {
|
||||
return []
|
||||
}
|
||||
let arr = []
|
||||
data.forEach((d, k) => {
|
||||
arr.push({
|
||||
x: d.timeframe,
|
||||
y: d.amount+1,
|
||||
})
|
||||
})
|
||||
return arr
|
||||
},
|
||||
async loadFailures() {
|
||||
this.loaded = false
|
||||
const data = await Api.service_failures_data(this.service.id, this.toUnix(this.parseISO(this.start)), this.toUnix(this.parseISO(this.end)), this.group, true)
|
||||
this.loaded = true
|
||||
this.data = [{data: this.convertChartData(data)}]
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -96,7 +96,7 @@ export default Vue.mixin({
|
|||
},
|
||||
serviceLink(service) {
|
||||
if (service.permalink) {
|
||||
service = this.$store.getters.serviceByPermalink(service.permalink)
|
||||
service = this.$store.getters.serviceById(service.permalink)
|
||||
}
|
||||
if (service === undefined || this.isEmptyObject(service)) {
|
||||
return `/service/0`
|
||||
|
@ -177,6 +177,12 @@ export default Vue.mixin({
|
|||
}
|
||||
return val + " μs"
|
||||
},
|
||||
humanTimeNum(val) {
|
||||
if (val >= 1000) {
|
||||
return Math.round(val / 1000)
|
||||
}
|
||||
return val
|
||||
},
|
||||
firstDayOfMonth(date) {
|
||||
return startOfMonth(date)
|
||||
},
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5">
|
||||
<div class="col-12 mb-4">
|
||||
|
||||
<div v-if="!ready" class="row mt-5">
|
||||
<div class="col-12 text-center">
|
||||
<font-awesome-icon icon="circle-notch" size="3x" spin/>
|
||||
</div>
|
||||
<div class="col-12 text-center mt-3 mb-3">
|
||||
<span class="text-muted">Loading Service</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-if="ready" class="col-12 mb-4">
|
||||
<span class="mt-3 mb-3 text-white d-md-none btn d-block d-md-none text-uppercase" :class="{'bg-success': service.online, 'bg-danger': !service.online}">
|
||||
{{service.online ? $t('online') : $t('offline')}}
|
||||
</span>
|
||||
|
@ -29,7 +40,7 @@
|
|||
<small class="d-block">To {{this.format(new Date(end_time))}}</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 mb-1 mb-md-0">
|
||||
<select :disabled="!loaded" @change="chartHits" v-model="group" class="form-control">
|
||||
<select :disabled="!loaded" @change="chartHits(service)" v-model="group" class="form-control">
|
||||
<option value="1m">1 Minute</option>
|
||||
<option value="5m">5 Minutes</option>
|
||||
<option value="15m">15 Minute</option>
|
||||
|
@ -51,12 +62,13 @@
|
|||
<div class="card text-black-50 bg-white mt-3 mb-3">
|
||||
<div class="card-header text-capitalize">Service Latency</div>
|
||||
<div v-if="loaded" class="card-body">
|
||||
<div class="row mb-5">
|
||||
<div class="row">
|
||||
<AdvancedChart :group="group" :updated="updated_chart" :start="start_time.toString()" :end="end_time.toString()" :service="service"/>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<apexchart height="220" type="rangeBar" :options="timeRangeOptions" :series="uptime_data"></apexchart>
|
||||
</div>
|
||||
<div>
|
||||
<FailuresBarChart :service="service" :start="start_time.toString()" :end="end_time.toString()" :group="group"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div v-else class="row mt-3 mb-3">
|
||||
<div class="col-12 text-center">
|
||||
|
@ -91,6 +103,7 @@
|
|||
|
||||
import flatPickr from 'vue-flatpickr-component';
|
||||
import 'flatpickr/dist/flatpickr.css';
|
||||
import FailuresBarChart from "@/components/Service/FailuresBarChart";
|
||||
const timeoptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||
|
||||
const axisOptions = {
|
||||
|
@ -120,6 +133,7 @@
|
|||
export default {
|
||||
name: 'Service',
|
||||
components: {
|
||||
FailuresBarChart,
|
||||
AdvancedChart,
|
||||
ServiceTopStats,
|
||||
ServiceHeatmap,
|
||||
|
@ -130,12 +144,13 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
service: null,
|
||||
id: null,
|
||||
tab: "failures",
|
||||
authenticated: false,
|
||||
ready: true,
|
||||
group: "1h",
|
||||
ready: false,
|
||||
group: "15m",
|
||||
data: null,
|
||||
service: null,
|
||||
uptime_data: null,
|
||||
loaded: false,
|
||||
messages: [],
|
||||
|
@ -154,8 +169,8 @@ export default {
|
|||
timeRangeOptions: {
|
||||
chart: {
|
||||
id: 'uptime',
|
||||
height: 120,
|
||||
width: "100%",
|
||||
height: 220,
|
||||
width: '100%',
|
||||
type: 'rangeBar',
|
||||
toolbar: {
|
||||
show: false
|
||||
|
@ -189,7 +204,7 @@ export default {
|
|||
type: 'datetime'
|
||||
},
|
||||
yaxis: {
|
||||
show: false
|
||||
show: true,
|
||||
},
|
||||
grid: {
|
||||
row: {
|
||||
|
@ -256,14 +271,14 @@ export default {
|
|||
},
|
||||
stroke: {
|
||||
show: false,
|
||||
curve: 'smooth',
|
||||
curve: 'stepline',
|
||||
lineCap: 'butt',
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
labels: {
|
||||
show: true
|
||||
format: 'MM yyyy'
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false
|
||||
|
@ -347,6 +362,9 @@ export default {
|
|||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route': 'fetchData'
|
||||
},
|
||||
computed: {
|
||||
core () {
|
||||
return this.$store.getters.core
|
||||
|
@ -354,9 +372,6 @@ export default {
|
|||
params () {
|
||||
return {start: this.toUnix(new Date(this.start_time)), end: this.toUnix(new Date(this.end_time))}
|
||||
},
|
||||
id () {
|
||||
return this.$route.params.id;
|
||||
},
|
||||
uptimeSeries () {
|
||||
return this.timedata.series
|
||||
},
|
||||
|
@ -370,19 +385,25 @@ export default {
|
|||
return this.$store.getters.serviceMessages(this.service.id).filter(m => this.inRange(m))
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route': 'reload',
|
||||
},
|
||||
created() {
|
||||
this.reload()
|
||||
this.fetchData()
|
||||
},
|
||||
async mounted() {
|
||||
if (!this.$store.getters.service) {
|
||||
// const s = await Api.service(this.id)
|
||||
// this.$store.commit('setService', s)
|
||||
}
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async fetchData () {
|
||||
if (!this.$route.params.id) {
|
||||
this.ready = false
|
||||
return
|
||||
}
|
||||
this.services = await Api.services()
|
||||
await this.$store.commit("setServices", this.services)
|
||||
|
||||
this.service = await Api.service(this.$route.params.id)
|
||||
await this.reload()
|
||||
this.ready = true
|
||||
},
|
||||
async updated_chart(start, end) {
|
||||
this.loaded = false
|
||||
this.start_time = start
|
||||
|
@ -390,19 +411,15 @@ export default {
|
|||
this.loaded = true
|
||||
},
|
||||
async reload() {
|
||||
this.loaded = false
|
||||
const services = await Api.services()
|
||||
this.$store.commit("setServices", services)
|
||||
if (this.isNumeric(this.$route.params.id)) {
|
||||
this.service = this.$store.getters.serviceById(this.$route.params.id)
|
||||
} else {
|
||||
this.service = this.$store.getters.serviceByPermalink(this.$route.params.id)
|
||||
}
|
||||
await this.chartHits()
|
||||
await this.chartFailures()
|
||||
await this.fetchUptime()
|
||||
this.loaded = true
|
||||
},
|
||||
async fetchUptime() {
|
||||
async fetchUptime(service) {
|
||||
if (service) {
|
||||
return
|
||||
}
|
||||
const uptime = await Api.service_uptime(this.service.id, this.params.start, this.params.end)
|
||||
this.uptime_data = this.parse_uptime(uptime)
|
||||
},
|
||||
|
@ -439,21 +456,31 @@ export default {
|
|||
inRange(message) {
|
||||
return this.isBetween(this.now(), message.start_on, message.start_on === message.end_on ? this.maxDate().toISOString() : message.end_on)
|
||||
},
|
||||
async getService() {
|
||||
await this.chartHits()
|
||||
await this.serviceFailures()
|
||||
},
|
||||
async serviceFailures() {
|
||||
this.failures = await Api.service_failures(this.service.id, this.params.start, this.params.end)
|
||||
},
|
||||
async chartHits(start=0, end=99999999999) {
|
||||
this.data = await Api.service_hits(this.service.id, this.params.start, this.params.end, this.group, false)
|
||||
if (this.data.length === 0 && this.group !== "1h") {
|
||||
this.group = "1h"
|
||||
await this.chartHits("1h")
|
||||
}
|
||||
this.ready = true
|
||||
async chartHits(start=0, end=99999999999) {
|
||||
if (!this.service) {
|
||||
return
|
||||
}
|
||||
this.data = await Api.service_hits(this.service.id, this.params.start, this.params.end, this.group, false)
|
||||
if (this.data.length === 0 && this.group !== "1h") {
|
||||
this.group = "1h"
|
||||
await this.chartHits(service, "1h")
|
||||
}
|
||||
this.ready = true
|
||||
},
|
||||
async chartFailures(start=0, end=99999999999) {
|
||||
if (!this.service) {
|
||||
return
|
||||
}
|
||||
this.failures_data = await Api.service_failures_data(this.service.id, this.params.start, this.params.end, this.group, true)
|
||||
if (this.data.length === 0 && this.group !== "1h") {
|
||||
this.group = "1h"
|
||||
await this.chartFailures(service, "1h")
|
||||
}
|
||||
this.ready = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -70,10 +70,7 @@ export default new Vuex.Store({
|
|||
}
|
||||
},
|
||||
serviceById: (state) => (id) => {
|
||||
return state.services.find(s => s.id === id)
|
||||
},
|
||||
serviceByPermalink: (state) => (permalink) => {
|
||||
return state.services.find(s => s.permalink === permalink)
|
||||
return state.services.find(s => s.permalink === id || s.id === id)
|
||||
},
|
||||
servicesInGroup: (state) => (id) => {
|
||||
return state.services.filter(s => s.group_id === id).sort((a, b) => a.order_id - b.order_id)
|
||||
|
|
Loading…
Reference in New Issue