service view page updates, chart updates

pull/792/head
hunterlong 2020-08-19 00:36:32 -07:00
parent 9ebd8e169f
commit c068401690
7 changed files with 272 additions and 64 deletions

View File

@ -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

View File

@ -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>

View File

@ -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)
}
}
}

View File

@ -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>

View File

@ -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)
},

View File

@ -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>

View File

@ -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)