Files
raven-client/ayanova/src/views/ops-metrics.vue
2020-06-19 23:58:52 +00:00

569 lines
15 KiB
Vue

<template>
<v-row v-if="this.formState.ready" v-resize="onResize">
<gz-error :errorBoxMessage="formState.errorBoxMessage"></gz-error>
<v-col cols="12" sm="6" lg="4" xl="3">
<v-select
v-model="selectedTimePeriod"
:items="selectLists.dateFilterTokens"
item-text="name"
item-value="id"
append-outer-icon="fa-sync"
@click:append-outer="getDataFromApi"
@input="timePeriodChanged"
></v-select>
</v-col>
<v-tabs v-model="tab" color="primary" grow @change="tabChanged">
<v-tabs-slider></v-tabs-slider>
<v-tab>{{ $ay.t("MetricCPUMemory") }}</v-tab>
<v-tab>{{ $ay.t("Database") }}</v-tab>
<v-tab>{{ $ay.t("MetricFileStorage") }}</v-tab>
<v-tabs-items v-model="tab">
<v-tab-item>
<v-col cols="12">
<gz-chart-line
:chartData="memAllChartData"
:options="timeLineChartOptions"
class="my-12"
/>
</v-col>
<v-col cols="12">
<gz-chart-line
:chartData="cpuChartData"
:options="timeLineChartOptions"
class="my-12"
/>
</v-col>
</v-tab-item>
<v-tab-item>
<v-col cols="12">
<gz-chart-line
:chartData="dbChartData"
:options="timeLineChartOptions"
class="my-12"
/>
</v-col>
<v-col cols="12" lg="10" xl="8">
<gz-chart-bar-horizontal
:chartData="dbTopTablesChartData"
:options="{
responsive: true,
scales: {
xAxes: [{ gridLines: { display: true } }],
yAxes: [{ gridLines: { display: false } }]
}
}"
class="my-12"
/>
</v-col>
</v-tab-item>
<v-tab-item>
<v-col cols="12">
<gz-chart-line
:chartData="fileSizeChartData"
:options="timeLineChartOptions"
class="my-12"
/>
</v-col>
<v-col cols="12">
<gz-chart-line
:chartData="attachmentCountChartData"
:options="timeLineChartOptions"
class="my-12"
/>
</v-col>
</v-tab-item>
</v-tabs-items>
</v-tabs>
</v-row>
</template>
<script>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Xeslint-disable */
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//https://blog.hubspot.com/marketing/types-of-graphs-for-data-visualization
//https://medium.com/javascript-in-plain-english/exploring-chart-js-e3ba70b07aa4
import relativeDatefilterCalculator from "../api/relative-date-filter-calculator.js";
const FORM_KEY = "ops-metrics";
//Pleasing combo of contrasting colors for lines
//from http://perceptualedge.com/articles/b-eye/choosing_colors.pdf
const CHART_PALETTE = {
blue: "#1f77b4",
red: "#d62728",
orange: "#fe7f0e",
green: "#2ca02c",
purple: "#9c27b0",
black: "#000000",
cyan: "#00BCD4",
teal: "#009688",
primary: "#00205B", //APP Canucks dark blue
secondary: "#00843D", //APP canucks green
accent: "#db7022" //APP lighter orangey red, more friendly looking though not as much clarity it seems
};
export default {
created() {
let vm = this;
initForm(vm)
.then(() => {
window.$gz.eventBus.$on("menu-click", clickHandler);
generateMenu(vm);
vm.getDataFromApi();
})
.catch(err => {
vm.formState.ready = true;
window.$gz.errorHandler.handleFormError(err, vm);
});
},
beforeDestroy() {
window.$gz.eventBus.$off("menu-click", clickHandler);
},
data() {
return {
storage: { isnew: true },
memcpu: { isnew: true },
db: { isnew: true },
timeLineChartOptions: {
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [
{
type: "time",
distribution: "linear",
gridLines: {
drawOnChartArea: false
}
}
],
yAxes: [
{
gridLines: {
drawOnChartArea: false
}
}
]
}
},
selectedTimePeriod: "*past6hours*",
selectLists: {
dateFilterTokens: []
},
lastFetchBreakPoint: null,
tab: 0,
formState: {
ready: false,
loading: false,
errorBoxMessage: null,
appError: null,
serverError: {}
}
};
},
methods: {
onResize() {
let breakPoint = "xl";
if (window.innerWidth < 1904) {
breakPoint = "lg";
}
if (window.innerWidth < 1264) {
breakPoint = "md";
}
if (window.innerWidth < 960) {
breakPoint = "sm";
}
if (window.innerWidth < 600) {
breakPoint = "xs";
}
if (breakPoint != this.lastFetchBreakPoint) {
this.getDataFromApi(breakPoint);
}
},
timePeriodChanged: function() {
this.getDataFromApi();
},
tabChanged: function(tab) {
let vm = this;
if (vm[tabIndexToRoute(vm.tab)].isnew) {
vm.getDataFromApi();
}
},
getDataFromApi(breakPoint) {
let vm = this;
if (vm.formState.loading) {
return;
}
let filterDates = relativeDatefilterCalculator.tokenToDates(
vm.selectedTimePeriod
);
vm.formState.loading = true;
if (breakPoint == null) {
breakPoint = vm.$vuetify.breakpoint.name;
}
//##########################################################################################################################################
//Visually, 200 points max looks best on full screen widths and 40 on galaxy s9 minimum standard
//there is no relevant loss of fidelity with this downsampling
let max = 200;
//https://vuetifyjs.com/en/customization/breakpoints/#breakpoint-service-object
//Note: used geometric progression of 1.5 for this
//40*1.5=60, 60*1.5=90 etc etc etc
//wanted it to fit between 40 at lowest end and 200 at highest end
switch (breakPoint) {
case "xs":
max = 40;
break;
case "sm":
max = 60;
break;
case "md":
max = 90;
break;
case "lg":
max = 135;
break;
case "xl":
max = 200;
break;
default:
max = 200;
}
let url =
"server-metric/" +
tabIndexToRoute(vm.tab) +
"?maxRecords=" +
max +
"&tsStart=" +
filterDates.after +
"&tsEnd=" +
filterDates.before;
window.$gz.form.deleteAllErrorBoxErrors(vm);
window.$gz.api
.get(url)
.then(res => {
if (res.error) {
if (res.error.code == "2010") {
window.$gz.eventBus.$emit(
"notify-error",
vm.$ay.t("ErrorAPI2010")
);
window.$gz._.delay(function() {
vm.$router.go(-1);
}, 2000);
}
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
vm[tabIndexToRoute(vm.tab)] = res.data;
vm.lastFetchBreakPoint = breakPoint;
window.$gz.form.setFormState({
vm: vm,
dirty: false,
valid: true,
loading: false
});
vm.formState.ready = true;
}
})
.catch(function handleGetDataFromAPIError(error) {
window.$gz.form.setFormState({
vm: vm,
loading: false
});
window.$gz.errorHandler.handleFormError(error, vm);
});
}
},
computed: {
cpuChartData() {
return {
datasets: [
{
label: "CPU %",
borderColor: CHART_PALETTE.teal,
fill: false,
radius: 0,
hoverRadius: 10,
hitRadius: 4,
data: this.memcpu.cpu
}
]
};
},
dbChartData() {
return {
datasets: [
{
label: this.$ay.t("MetricDBSize"),
borderColor: CHART_PALETTE.blue,
fill: false,
radius: 0,
hoverRadius: 10,
hitRadius: 4,
data: this.db.totalSize
}
]
};
},
dbTopTablesChartData() {
// debugger;
return {
// These labels appear in the legend and in the tooltips when hovering different arcs
//_.map(users, 'user');
labels: window.$gz._.map(this.db.topTables, "name"),
datasets: [
{
label: this.$ay.t("MetricTopTablesSize"),
data: window.$gz._.map(this.db.topTables, "value"),
backgroundColor: getPalette(
this.db.topTables ? this.db.topTables.length : 3
),
minBarLength: 5
}
]
};
},
memAllChartData() {
return {
datasets: [
{
label: this.$ay.t("MetricAllocatedMemory"),
borderColor: CHART_PALETTE.purple,
fill: false,
radius: 0,
hoverRadius: 10,
hitRadius: 4,
data: this.memcpu.allocated
},
{
label: this.$ay.t("MetricWorkingSet"),
borderColor: CHART_PALETTE.green,
//borderWidth:4,
// pointStyle: "rectRot",
radius: 0,
hoverRadius: 10,
hitRadius: 4,
// borderDash: [20, 3, 3, 3, 3, 3, 3, 3],
fill: false,
data: this.memcpu.workingSet
},
{
label: this.$ay.t("MetricPrivateBytes"),
borderColor: CHART_PALETTE.orange,
fill: false,
radius: 0,
hoverRadius: 10,
hitRadius: 4,
data: this.memcpu.privateBytes
}
]
};
},
fileSizeChartData() {
return {
datasets: [
{
label: this.$ay.t("MetricAttachmentsMB"),
borderColor: CHART_PALETTE.purple,
fill: false,
radius: 0,
hoverRadius: 10,
hitRadius: 4,
data: this.storage.attachmentFileSize
},
{
label: this.$ay.t("MetricBackupMB"),
borderColor: CHART_PALETTE.orange,
fill: false,
radius: 0,
hoverRadius: 10,
hitRadius: 4,
data: this.storage.utilityFileSize
},
{
label: this.$ay.t("MetricAvailableDiskSpace"),
borderColor: CHART_PALETTE.green,
fill: false,
radius: 0,
hoverRadius: 10,
hitRadius: 4,
data: this.storage.attachmentFilesAvailableSpace
}
]
};
},
attachmentCountChartData() {
return {
datasets: [
{
label: this.$ay.t("MetricAttachmentsCount"),
borderColor: CHART_PALETTE.blue,
fill: false,
radius: 0,
hoverRadius: 10,
hitRadius: 4,
data: this.storage.attachmentFileCount
}
]
};
}
}
};
///////////////////////
//
function getPalette(size) {
let palette = [
CHART_PALETTE.blue,
CHART_PALETTE.red,
CHART_PALETTE.green,
CHART_PALETTE.orange,
CHART_PALETTE.purple,
CHART_PALETTE.cyan,
CHART_PALETTE.teal,
CHART_PALETTE.black
];
let paletteLength = palette.length;
let ret = [];
for (let i = 0; i < size; i++) {
ret.push(palette[i % paletteLength]);
}
return ret;
}
//////////////////////
//
//
function tabIndexToRoute(tabIndex) {
switch (tabIndex) {
case 0:
return "memcpu";
case 1:
return "db";
case 2:
return "storage";
}
}
//////////////////////
//
//
function generateMenu(vm) {
let menuOptions = {
isMain: true,
icon: "fa-file-medical-alt",
title: vm.$ay.t("ServerMetrics"),
helpUrl: "form-ops-metrics",
formData: {
ayaType: window.$gz.type.ServerMetrics
},
menuItems: [
// {
// title: "DEV_TEST_COLLECT",
// icon: "fa-bolt",
// surface: true,
// key: FORM_KEY + ":collect",
// vm: vm
// }
]
};
window.$gz.eventBus.$emit("menu-change", menuOptions);
}
/////////////////////////////
//
//
function clickHandler(menuItem) {
if (!menuItem) {
return;
}
let m = window.$gz.menu.parseMenuItem(menuItem);
if (m.owner == FORM_KEY && !m.disabled) {
switch (m.key) {
// case "collect":
// //trigger garbage collection
// window.$gz.api.get("server-metric/collect");
// break;
default:
window.$gz.eventBus.$emit(
"notify-warning",
FORM_KEY + "::context click: [" + m.key + "]"
);
}
}
}
/////////////////////////////////
//
//
function initForm(vm) {
return new Promise(function(resolve, reject) {
(async function() {
try {
await fetchTranslatedText(vm);
await populateSelectionLists(vm);
} catch (err) {
reject(err);
}
resolve();
})();
});
}
/////////////////////////////////
//
//
function populateSelectionLists(vm) {
vm.selectLists.dateFilterTokens.push(
...[
{ name: vm.$ay.t("DateRangePast6Hours"), id: "*past6hours*" },
{ name: vm.$ay.t("DateRangePast24Hours"), id: "*past24hours*" },
{ name: vm.$ay.t("DateRangePast7Days"), id: "*past7days*" },
{ name: vm.$ay.t("DateRangePast30Days"), id: "*past30days*" },
{
name: vm.$ay.t("DateRangeInTheLastSixMonths"),
id: "*last6months*"
},
{ name: vm.$ay.t("DateRangePastYear"), id: "*pastyear*" }
]
);
}
//////////////////////////////////////////////////////////
//
// Ensures UI translated text is available
//
async function fetchTranslatedText(vm) {
await window.$gz.translation.cacheTranslations([
"DateRangePast6Hours",
"DateRangePast24Hours",
"DateRangePast7Days",
"DateRangePast30Days",
"DateRangeInTheLastSixMonths",
"DateRangePastYear",
"MetricFileStorage",
"MetricAttachmentsMB",
"MetricBackupMB",
"MetricAvailableDiskSpace",
"MetricAttachmentsCount",
"Database",
"MetricDBSize",
"MetricTopTablesSize",
"MetricCPUMemory",
"MetricAllocatedMemory",
"MetricWorkingSet",
"MetricPrivateBytes"
]);
}
</script>