add metric config

This commit is contained in:
Qiuxia Fan 2022-03-25 20:51:50 +08:00
parent aa91cbfe53
commit 19715d7152
14 changed files with 222 additions and 196 deletions

View File

@ -0,0 +1,17 @@
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M6.984 9.984h10.031l-5.016 5.016z"></path>
</svg>

After

Width:  |  Height:  |  Size: 922 B

View File

@ -13,6 +13,5 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>cancel</title>
<path d="M17.016 15.609l-3.609-3.609 3.609-3.609-1.406-1.406-3.609 3.609-3.609-3.609-1.406 1.406 3.609 3.609-3.609 3.609 1.406 1.406 3.609-3.609 3.609 3.609zM12 2.016q4.125 0 7.055 2.93t2.93 7.055-2.93 7.055-7.055 2.93-7.055-2.93-2.93-7.055 2.93-7.055 7.055-2.93z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -13,6 +13,5 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>clearclose</title>
<path d="M18.984 6.422l-5.578 5.578 5.578 5.578-1.406 1.406-5.578-5.578-5.578 5.578-1.406-1.406 5.578-5.578-5.578-5.578 1.406-1.406 5.578 5.578 5.578-5.578z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -13,6 +13,5 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<title>createmode_editedit</title>
<path d="M20.719 7.031l-1.828 1.828-3.75-3.75 1.828-1.828q0.281-0.281 0.703-0.281t0.703 0.281l2.344 2.344q0.281 0.281 0.281 0.703t-0.281 0.703zM3 17.25l11.063-11.063 3.75 3.75-11.063 11.063h-3.75v-3.75z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -22,6 +22,14 @@ export enum MetricQueryTypes {
READHEATMAP = "readHeatMap",
ReadSampledRecords = "readSampledRecords",
}
export enum Calculations {
Percentage = "percentage",
ByteToKB = "byteToKB",
Apdex = "apdex",
ConvertSeconds = "convertSeconds",
ConvertMilliseconds = "convertMilliseconds",
}
export enum sizeEnum {
XS = "XS",
SM = "SM",

View File

@ -15,13 +15,13 @@
* limitations under the License.
*/
import dayjs from "dayjs";
import { RespFields, MetricQueryTypes } from "./data";
import { RespFields, MetricQueryTypes, Calculations } from "./data";
import { ElMessage } from "element-plus";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app";
import { Instance, Endpoint, Service } from "@/types/selector";
import { StandardConfig } from "@/types/dashboard";
import { MetricConfigOpt } from "@/types/dashboard";
export function useQueryProcessor(config: any) {
if (!(config.metrics && config.metrics[0])) {
@ -48,6 +48,7 @@ export function useQueryProcessor(config: any) {
}
const fragment = config.metrics.map((name: string, index: number) => {
const metricType = config.metricTypes[index] || "";
const c = (config.metricConfig && config.metricConfig[index]) || {};
if (
[
MetricQueryTypes.ReadSampledRecords,
@ -63,7 +64,7 @@ export function useQueryProcessor(config: any) {
normal: selectorStore.currentService.normal,
scope: dashboardStore.entity,
topN: 10,
order: config.standard.sortOrder || "DES",
order: c.sortOrder || "DES",
};
} else {
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
@ -128,7 +129,7 @@ export function useSourceProcessor(
config: {
metrics: string[];
metricTypes: string[];
standard: StandardConfig;
metricConfig: MetricConfigOpt[];
}
) {
if (resp.errors) {
@ -140,23 +141,24 @@ export function useSourceProcessor(
config.metricTypes.forEach((type: string, index) => {
const m = config.metrics[index];
const c = (config.metricConfig && config.metricConfig[index]) || {};
if (type === MetricQueryTypes.ReadMetricsValues) {
source[m] = resp.data[keys[index]].values.values.map(
(d: { value: number }) => aggregation(d.value, config.standard)
(d: { value: number }) => aggregation(d.value, c)
);
}
if (type === MetricQueryTypes.ReadLabeledMetricsValues) {
const resVal = Object.values(resp.data)[0] || [];
const labels = (config.standard.metricLabels || "")
const labels = (c.label || "")
.split(",")
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
const labelsIdx = (config.standard.labelsIndex || "")
const labelsIdx = (c.labelsIndex || "")
.split(",")
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
for (const item of resVal) {
const values = item.values.values.map((d: { value: number }) =>
aggregation(Number(d.value), config.standard)
aggregation(Number(d.value), c)
);
const indexNum = labelsIdx.findIndex((d: string) => d === item.label);
@ -168,10 +170,7 @@ export function useSourceProcessor(
}
}
if (type === MetricQueryTypes.ReadMetricsValue) {
source[m] = aggregation(
Number(Object.values(resp.data)[0]),
config.standard
);
source[m] = aggregation(Number(Object.values(resp.data)[0]), c);
}
if (
type === MetricQueryTypes.SortMetrics ||
@ -179,7 +178,7 @@ export function useSourceProcessor(
) {
source[m] = (Object.values(resp.data)[0] || []).map(
(d: { value: unknown; name: string }) => {
d.value = aggregation(Number(d.value), config.standard);
d.value = aggregation(Number(d.value), c);
return d;
}
@ -307,33 +306,28 @@ export function useQueryTopologyMetrics(metrics: string[], ids: string[]) {
return { queryStr, conditions };
}
function aggregation(val: number, standard: any): number | string {
function aggregation(val: number, config: any): number | string {
let data: number | string = val;
if (!isNaN(standard.plus)) {
data = val + Number(standard.plus);
return data;
switch (config.calculation) {
case Calculations.Percentage:
data = val / 100;
break;
case Calculations.ByteToKB:
data = val / 1024;
break;
case Calculations.Apdex:
data = val / 10000;
break;
case Calculations.ConvertSeconds:
data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
break;
case Calculations.ConvertMilliseconds:
data = dayjs.unix(val).format("YYYY-MM-DD HH:mm:ss");
break;
default:
data;
break;
}
if (!isNaN(standard.minus)) {
data = val - Number(standard.plus);
return data;
}
if (!isNaN(standard.multiply) && standard.divide !== 0) {
data = val * Number(standard.multiply);
return data;
}
if (!isNaN(standard.divide) && standard.divide !== 0) {
data = val / Number(standard.divide);
return data;
}
if (standard.milliseconds) {
data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
return data;
}
if (standard.milliseconds) {
data = dayjs.unix(val).format("YYYY-MM-DD HH:mm:ss");
return data;
}
return data;
}

View File

@ -127,6 +127,7 @@ const msg = {
kubernetes: "Kubernetes",
textUrl: "Text Hyperlink",
textAlign: "Text Align",
metricLabel: "Metric Label",
hourTip: "Select Hour",
minuteTip: "Select Minute",
secondTip: "Select Second",
@ -259,7 +260,7 @@ const msg = {
independentSelector: "Selectors",
unknownMetrics: "Unknown Metrics",
labels: "Labels",
aggregation: "Data Calculation",
aggregation: "Calculation",
unit: "Unit",
labelsIndex: "Label Subscript",
parentService: "Parent Service",

View File

@ -127,6 +127,7 @@ const msg = {
kubernetes: "Kubernetes",
textUrl: "文本超链接",
textAlign: "文本对齐",
metricLabel: "指标标签",
hourTip: "选择小时",
minuteTip: "选择分钟",
secondTip: "选择秒数",
@ -261,7 +262,7 @@ const msg = {
independentSelector: "独立选择器",
unknownMetrics: "未知指标",
labels: "标签",
aggregation: "数据计算",
aggregation: "计算",
unit: "单位",
labelsIndex: "标签下标",
parentService: "父级服务",

View File

@ -28,6 +28,7 @@ export const NewControl = {
standard: {},
metrics: [""],
metricTypes: [""],
metricConfig: [],
};
export const TextConfig = {
fontColor: "white",

View File

@ -36,8 +36,17 @@ export interface LayoutConfig {
metricTypes: string[];
children?: any;
activedTabIndex?: number;
metricConfig?: MetricConfigOpt[];
}
export type MetricConfigOpt = {
unit: string;
label: string;
calculation: string;
labelsIndex: string;
sortOrder: string;
};
export interface WidgetConfig {
title?: string;
tips?: string;

View File

@ -49,6 +49,14 @@ limitations under the License. -->
@change="changeMetricType(index, $event)"
class="selectors"
/>
<el-popover placement="top" :width="400" :visible="showConfig">
<template #reference>
<span @click="setMetricConfig(index)">
<Icon class="cp mr-5" iconName="mode_edit" size="middle" />
</span>
</template>
<StandardOptions @update="queryMetrics" @close="showConfig = false" />
</el-popover>
<span
v-show="states.isList || states.metricTypes[0] === 'readMetricsValues'"
>
@ -101,13 +109,15 @@ import { ElMessage } from "element-plus";
import Icon from "@/components/Icon.vue";
import { useQueryProcessor, useSourceProcessor } from "@/hooks/useProcessor";
import { useI18n } from "vue-i18n";
import { DashboardItem } from "@/types/dashboard";
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
import StandardOptions from "./StandardOptions.vue";
/*global defineEmits */
const { t } = useI18n();
const emit = defineEmits(["update", "loading", "changeOpt"]);
const dashboardStore = useDashboardStore();
const { metrics, metricTypes, graph } = dashboardStore.selectedGrid;
const showConfig = ref<boolean>(false);
const states = reactive<{
metrics: string[];
metricTypes: string[];
@ -127,6 +137,13 @@ const states = reactive<{
dashboardName: graph.dashboardName,
dashboardList: [{ label: "", value: "" }],
});
const currentMetricConfig = ref<MetricConfigOpt>({
unit: "",
label: "",
labelsIndex: "",
calculation: "",
sortOrder: "DES",
});
states.isList = ListChartTypes.includes(graph.type);
const defaultLen = ref<number>(states.isList ? 5 : 20);
@ -323,8 +340,8 @@ async function queryMetrics() {
if (states.isList) {
return;
}
const { standard } = dashboardStore.selectedGrid;
const params = useQueryProcessor({ ...states, standard });
const { metricConfig } = dashboardStore.selectedGrid;
const params = useQueryProcessor({ ...states, metricConfig });
if (!params) {
emit("update", {});
return;
@ -337,7 +354,7 @@ async function queryMetrics() {
ElMessage.error(json.errors);
return;
}
const source = useSourceProcessor(json, { ...states, standard });
const source = useSourceProcessor(json, { ...states, metricConfig });
emit("update", source);
}
@ -393,6 +410,28 @@ function setMetricTypeList(type: string) {
}
return MetricTypes[type];
}
function setMetricConfig(index: number) {
showConfig.value = true;
const n = {
unit: "",
label: "",
calculation: "",
labelsIndex: "",
sortOrder: "DES",
};
if (
!dashboardStore.selectedGrid.metricConfig ||
!dashboardStore.selectedGrid.metricConfig[index]
) {
currentMetricConfig.value = n;
return;
}
currentMetricConfig.value = {
...n,
...dashboardStore.selectedGrid.metricConfig[index],
};
showConfig.value = true;
}
</script>
<style lang="scss" scoped>
.ds-name {

View File

@ -13,174 +13,125 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="item">
<span class="label">{{ t("unit") }}</span>
<el-input
class="input"
v-model="selectedGrid.standard.unit"
size="small"
placeholder="Please input Unit"
/>
</div>
<div class="item">
<span class="label">{{ t("sortOrder") }}</span>
<Selector
:value="sortOrder"
:options="SortOrder"
size="small"
placeholder="Select a sort order"
class="selector"
@change="changeStandardOpt({ sortOrder })"
/>
</div>
<div class="item">
<span class="label">{{ t("labels") }}</span>
<el-input
class="input"
v-model="selectedGrid.standard.metricLabels"
size="small"
placeholder="auto"
@change="changeStandardOpt"
/>
</div>
<div class="item">
<span class="label">{{ t("labelsIndex") }}</span>
<el-input
class="input"
v-model="selectedGrid.standard.labelsIndex"
size="small"
placeholder="auto"
@change="changeStandardOpt"
/>
</div>
<div class="item">
<span class="label">{{ t("plus") }}</span>
<el-input-number
class="input"
v-model="selectedGrid.standard.plus"
:min="0"
size="small"
placeholder="Please input"
@change="changeStandardOpt"
/>
</div>
<div class="item">
<span class="label">{{ t("minus") }}</span>
<el-input-number
class="input"
v-model="selectedGrid.standard.minus"
:min="0"
size="small"
placeholder="Please input"
@change="changeStandardOpt"
/>
</div>
<div class="item">
<span class="label">{{ t("multiply") }}</span>
<el-input-number
class="input"
v-model="selectedGrid.standard.multiply"
:min="1"
size="small"
placeholder="Please input"
@change="changeStandardOpt"
/>
</div>
<div class="item">
<span class="label">{{ t("divide") }}</span>
<el-input-number
class="input"
v-model="selectedGrid.standard.divide"
size="small"
placeholder="Please input"
:min="1"
@change="changeStandardOpt"
/>
</div>
<div class="item">
<span class="label">{{ t("convertToMilliseconds") }}</span>
<el-input-number
class="input"
:min="0"
v-model="selectedGrid.standard.milliseconds"
size="small"
placeholder="Please input"
@change="changeStandardOpt"
/>
</div>
<div class="item">
<span class="label">{{ t("convertToSeconds") }}</span>
<el-input-number
class="input"
:min="0"
v-model="selectedGrid.standard.seconds"
size="small"
placeholder="Please input"
@change="changeStandardOpt"
<div class="config-panel">
<Icon
class="cp mr-5 close"
iconName="cancel"
size="middle"
@click="closePopper"
/>
<div class="item mb-10">
<span class="label">{{ t("unit") }}</span>
<el-input
class="input"
v-model="currentMetric.unit"
size="small"
placeholder="Please input unit"
@change="changeConfigs(index, { unit: currentMetricConfig.unit })"
/>
</div>
<div class="item mb-10">
<span class="label">{{ t("metricLabel") }}</span>
<el-input
class="input"
v-model="currentMetric.label"
size="small"
placeholder="Please input a name"
@change="changeConfigs(index, { label: currentMetricConfig.label })"
/>
</div>
<div class="item mb-10">
<span class="label">{{ t("labelsIndex") }}</span>
<el-input
class="input"
v-model="currentMetric.labelsIndex"
size="small"
placeholder="auto"
@change="
changeConfigs(index, { label: currentMetricConfig.labelsIndex })
"
/>
</div>
<div class="item mb-10">
<span class="label">{{ t("aggregation") }}</span>
<Selector
:value="currentMetric.calculation"
:options="CalculationOpts"
size="small"
placeholder="Select a option"
@change="changeConfigs(index, { calculation: $event[0].value })"
class="aggregation"
:clearable="true"
/>
</div>
<div class="item">
<span class="label">{{ t("sortOrder") }}</span>
<Selector
:value="currentMetric.sortOrder || 'DES'"
:options="SortOrder"
size="small"
placeholder="Select a sort order"
class="aggregation"
@change="changeConfigs(index, { sortOrder: $event[0].value })"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
import { SortOrder } from "../../data";
import { SortOrder, CalculationOpts } from "../../data";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useQueryProcessor, useSourceProcessor } from "@/hooks/useProcessor";
import { ElMessage } from "element-plus";
import { MetricConfigOpt } from "@/types/dashboard";
/*global defineEmits */
/*global defineEmits, defineProps */
const props = defineProps({
currentMetricConfig: {
type: Object as PropType<MetricConfigOpt>,
default: () => ({ unit: "" }),
},
index: { type: Number, default: 0 },
});
const { t } = useI18n();
const emit = defineEmits(["update", "loading"]);
const emit = defineEmits(["update", "close"]);
const dashboardStore = useDashboardStore();
const { selectedGrid } = dashboardStore;
const sortOrder = ref<string>(selectedGrid.standard.sortOrder || "DES");
const currentMetric = ref<MetricConfigOpt>(props.currentMetricConfig);
function changeStandardOpt(param?: any) {
let standard = dashboardStore.selectedGrid.standard;
if (param) {
standard = {
...dashboardStore.selectedGrid.standard,
...param,
};
dashboardStore.selectWidget({ ...dashboardStore.selectedGrid, standard });
}
queryMetrics();
function changeConfigs(index: number, param: { [key: string]: string }) {
const metricConfig = dashboardStore.selectedGrid.metricConfig || [];
metricConfig[index] = { ...metricConfig[index], ...param };
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
metricConfig,
});
console.log(dashboardStore.selectedGrid);
emit("update");
}
async function queryMetrics() {
const params = useQueryProcessor(dashboardStore.selectedGrid);
if (!params) {
emit("update", {});
return;
}
emit("loading", true);
const json = await dashboardStore.fetchMetricValue(params);
emit("loading", false);
if (json.errors) {
ElMessage.error(json.errors);
return;
}
const source = useSourceProcessor(json, dashboardStore.selectedGrid);
emit("update", source);
function closePopper() {
emit("close");
}
</script>
<style lang="scss" scoped>
.config-panel {
padding: 10px 5px;
position: relative;
}
.label {
font-size: 13px;
font-weight: 500;
display: block;
margin-bottom: 5px;
width: 150px;
display: inline-block;
font-size: 12px;
}
.input {
width: 500px;
.close {
position: absolute;
top: -8px;
right: -15px;
}
.item {
margin-bottom: 10px;
}
.selector {
width: 500px;
.aggregation {
width: 365px;
}
</style>

View File

@ -128,7 +128,7 @@ export default defineComponent({
const d = {
metrics: props.data.metrics,
metricTypes: props.data.metricTypes,
standard: props.data.standard,
metricConfig: props.data.metricConfig || [],
};
state.source = useSourceProcessor(json, d);
}

View File

@ -264,3 +264,11 @@ export const TextColors: { [key: string]: string } = {
black: "#000",
orange: "#E6A23C",
};
export const CalculationOpts = [
{ label: "Percentage", value: "percentage" },
{ label: "ByteToKB", value: "byteToKB" },
{ label: "ConvertMilliseconds", value: "convertMilliseconds" },
{ label: "ConvertSeconds", value: "convertSeconds" },
{ label: "Apdex", value: "apdex" },
];