feat: visualize metrics in InstanceList

This commit is contained in:
Qiuxia Fan 2022-01-23 17:01:58 +08:00
parent e26380c359
commit 0f01ac34a9
10 changed files with 157 additions and 15 deletions

View File

@ -19,6 +19,7 @@ import { ElMessage } from "element-plus";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { useSelectorStore } from "@/store/modules/selectors"; import { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app"; import { useAppStoreWithOut } from "@/store/modules/app";
import { Instance } from "@/types/selector";
export function useQueryProcessor(config: any) { export function useQueryProcessor(config: any) {
if (!(config.metrics && config.metrics.length)) { if (!(config.metrics && config.metrics.length)) {
@ -191,3 +192,61 @@ function aggregation(json: {
} }
return json.data; return json.data;
} }
export function useQueryPodsMetrics(
pods: Instance[],
config: { metrics: string[]; metricTypes: string[] }
) {
const appStore = useAppStoreWithOut();
const selectorStore = useSelectorStore();
const conditions: { [key: string]: unknown } = {
duration: appStore.durationTime,
};
const variables: string[] = [`$duration: Duration!`];
const { currentService } = selectorStore;
const fragmentList = pods.map((d: Instance, index: number) => {
const param = {
scope: "ServiceInstance",
serviceName: currentService.label,
serviceInstanceName: d.label,
normal: currentService.normal,
};
const f = config.metrics.map((name: string, idx: number) => {
const metricType = config.metricTypes[idx] || "";
conditions[`condition${index}${idx}`] = {
name,
entity: param,
};
variables.push(`$condition${index}${idx}: MetricsCondition!`);
return `${name}${index}${idx}: ${metricType}(condition: $condition${index}${idx}, duration: $duration)${RespFields[metricType]}`;
});
return f;
});
const fragment = fragmentList.flat(1).join(" ");
const queryStr = `query queryData(${variables}) {${fragment}}`;
return { queryStr, conditions };
}
export function usePodsSource(
instances: Instance[],
resp: { errors: string; data: { [key: string]: any } },
config: { metrics: string[]; metricTypes: string[] }
): any {
if (resp.errors) {
ElMessage.error(resp.errors);
return {};
}
const data = instances.map((d: Instance | any, idx: number) => {
config.metrics.map((name: string, index: number) => {
const key = name + idx + index;
d[name] = resp.data[key].values.values.map(
(d: { value: number }) => d.value
);
});
return d;
});
return data;
}

View File

@ -25,6 +25,7 @@ import { NewControl } from "../data";
import { Duration } from "@/types/app"; import { Duration } from "@/types/app";
import axios, { AxiosResponse } from "axios"; import axios, { AxiosResponse } from "axios";
import { cancelToken } from "@/utils/cancelToken"; import { cancelToken } from "@/utils/cancelToken";
import { Instance } from "@/types/selector";
interface DashboardState { interface DashboardState {
showConfig: boolean; showConfig: boolean;
layout: LayoutConfig[]; layout: LayoutConfig[];
@ -179,7 +180,10 @@ export const dashboardStore = defineStore({
return res.data; return res.data;
}, },
async fetchMetricValue(param: { queryStr: string; conditions: any }) { async fetchMetricValue(param: {
queryStr: string;
conditions: { [key: string]: unknown };
}) {
const res: AxiosResponse = await axios.post( const res: AxiosResponse = await axios.post(
"/graphql", "/graphql",
{ query: param.queryStr, variables: { ...param.conditions } }, { query: param.queryStr, variables: { ...param.conditions } },

View File

@ -65,6 +65,8 @@ export interface LineConfig extends AreaConfig {
smooth?: boolean; smooth?: boolean;
showSymbol?: boolean; showSymbol?: boolean;
step?: boolean; step?: boolean;
showXAxis?: boolean;
showYAxis?: boolean;
} }
export interface AreaConfig { export interface AreaConfig {

View File

@ -22,3 +22,12 @@ export type Service = {
normal: boolean; normal: boolean;
group: string; group: string;
}; };
export type Instance = {
value: string;
label: string;
layer: string;
language: string;
instanceUUID: string;
attributes: { name: string; value: string }[];
};

View File

@ -61,7 +61,11 @@ limitations under the License. -->
<el-collapse-item :title="t('graphStyles')" name="3"> <el-collapse-item :title="t('graphStyles')" name="3">
<component <component
:is="`${states.graph.type}Config`" :is="`${states.graph.type}Config`"
:config="states.graph" :config="{
...states.graph,
metrics: states.metrics,
metricTypes: states.metricTypes,
}"
@update="updateGraphOptions" @update="updateGraphOptions"
/> />
</el-collapse-item> </el-collapse-item>

View File

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div v-show="states.isTable" class="ds-name"> <div v-show="states.isTable" class="ds-name">
<div>Dashboard</div>
<Selector <Selector
:value="states.graph.dashboardName" :value="states.graph.dashboardName"
:options="states.metricList" :options="states.metricList"
@ -28,6 +29,7 @@ limitations under the License. -->
:key="index" :key="index"
class="metric-item" class="metric-item"
> >
<div>Metrics</div>
<Selector <Selector
:value="metric" :value="metric"
:options="states.metricList" :options="states.metricList"
@ -140,6 +142,10 @@ function changeMetrics(index: number, arr: (Option & { type: string })[]) {
states.metricTypeList = []; states.metricTypeList = [];
states.metricTypes = []; states.metricTypes = [];
emit("apply", { metricTypes: states.metricTypes }); emit("apply", { metricTypes: states.metricTypes });
dashboardStore.selectWidget({
...selectedGrid,
...{ metricTypes: states.metricTypes, metrics: states.metrics },
});
return; return;
} }
states.metrics[index] = arr[0].value; states.metrics[index] = arr[0].value;
@ -147,6 +153,10 @@ function changeMetrics(index: number, arr: (Option & { type: string })[]) {
states.metricTypeList[index] = MetricTypes[typeOfMetrics]; states.metricTypeList[index] = MetricTypes[typeOfMetrics];
states.metricTypes[index] = MetricTypes[typeOfMetrics][0].value; states.metricTypes[index] = MetricTypes[typeOfMetrics][0].value;
dashboardStore.selectWidget({
...selectedGrid,
...{ metricTypes: states.metricTypes, metrics: states.metrics },
});
emit("apply", { metricTypes: states.metricTypes, metrics: states.metrics }); emit("apply", { metricTypes: states.metricTypes, metrics: states.metrics });
queryMetrics(); queryMetrics();
} }
@ -170,6 +180,10 @@ function changeMetricType(index: number, opt: Option[]) {
return d; return d;
}); });
} }
dashboardStore.selectWidget({
...selectedGrid,
...{ metricTypes: states.metricTypes },
});
emit("apply", { metricTypes: states.metricTypes }); emit("apply", { metricTypes: states.metricTypes });
queryMetrics(); queryMetrics();
} }

View File

@ -47,7 +47,11 @@ limitations under the License. -->
:is="data.graph.type" :is="data.graph.type"
:intervalTime="appStore.intervalTime" :intervalTime="appStore.intervalTime"
:data="state.source" :data="state.source"
:config="data.graph" :config="{
...data.graph,
metrics: data.metrics,
metricTypes: data.metricTypes,
}"
:standard="data.standard" :standard="data.standard"
/> />
</div> </div>

View File

@ -46,9 +46,19 @@ limitations under the License. -->
</router-link> </router-link>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="metric"> <el-table-column
v-for="(metric, index) in selectedGrid.metrics"
:label="metric"
:key="metric + index"
>
<template #default="scope"> <template #default="scope">
{{ scope.row.label }} <div class="chart">
<Line
:data="{ metric: scope.row[metric] }"
:intervalTime="intervalTime"
:config="{ showXAxis: false, showYAxis: false }"
/>
</div>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -65,27 +75,38 @@ limitations under the License. -->
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onBeforeMount, ref } from "vue"; import { ref } from "vue";
import { useSelectorStore } from "@/store/modules/selectors";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import type { PropType } from "vue"; import type { PropType } from "vue";
import Line from "./Line.vue";
import { useSelectorStore } from "@/store/modules/selectors";
import { useDashboardStore } from "@/store/modules/dashboard";
import { InstanceListConfig } from "@/types/dashboard"; import { InstanceListConfig } from "@/types/dashboard";
import { Instance } from "@/types/selector";
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
/*global defineProps */ /*global defineProps */
defineProps({ defineProps({
config: { config: {
type: Object as PropType<InstanceListConfig>, type: Object as PropType<
InstanceListConfig & { metrics: string[]; metricTypes: string[] }
>,
default: () => ({ dashboardName: "", fontSize: 12 }), default: () => ({ dashboardName: "", fontSize: 12 }),
}, },
intervalTime: { type: Array as PropType<string[]>, default: () => [] },
}); });
const selectorStore = useSelectorStore(); const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore();
const chartLoading = ref<boolean>(false); const chartLoading = ref<boolean>(false);
const instances = ref<{ layer: string; label: string }[]>([]); const instances = ref<(Instance | any)[]>([]); // current instances
const searchInstances = ref<{ layer: string; label: string }[]>([]); const searchInstances = ref<Instance[]>([]); // all instances
const pageSize = 7; const pageSize = 5;
const searchText = ref<string>(""); const searchText = ref<string>("");
const selectedGrid = dashboardStore.selectedGrid;
onBeforeMount(async () => { queryInstance();
async function queryInstance() {
chartLoading.value = true; chartLoading.value = true;
const resp = await selectorStore.getServiceInstances(); const resp = await selectorStore.getServiceInstances();
@ -95,8 +116,19 @@ onBeforeMount(async () => {
return; return;
} }
searchInstances.value = selectorStore.instances; searchInstances.value = selectorStore.instances;
instances.value = searchInstances.value.splice(0, pageSize);
}); const currentInstances = searchInstances.value.splice(0, pageSize);
const params = await useQueryPodsMetrics(currentInstances, selectedGrid);
const json = await dashboardStore.fetchMetricValue(params);
if (json.errors) {
ElMessage.error(json.errors);
return;
}
usePodsSource(currentInstances, json, selectedGrid);
instances.value = usePodsSource(currentInstances, json, selectedGrid);
}
function changePage(pageIndex: number) { function changePage(pageIndex: number) {
instances.value = searchInstances.value.splice(pageIndex - 1, pageSize); instances.value = searchInstances.value.splice(pageIndex - 1, pageSize);
} }
@ -109,4 +141,8 @@ function searchList() {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "./style.scss"; @import "./style.scss";
.chart {
height: 39px;
}
</style> </style>

View File

@ -20,6 +20,7 @@ import { computed } from "vue";
import type { PropType } from "vue"; import type { PropType } from "vue";
import { Event } from "@/types/events"; import { Event } from "@/types/events";
import { LineConfig } from "@/types/dashboard"; import { LineConfig } from "@/types/dashboard";
import { config } from "@vue/test-utils";
/*global defineProps */ /*global defineProps */
const props = defineProps({ const props = defineProps({
@ -37,6 +38,8 @@ const props = defineProps({
smooth: false, smooth: false,
showSymbol: false, showSymbol: false,
opacity: 0.4, opacity: 0.4,
showXAxis: true,
showYAxis: true,
}), }),
}, },
}); });
@ -143,6 +146,8 @@ function getOption() {
color, color,
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
zlevel: 1000,
z: 60,
backgroundColor: "rgb(50,50,50)", backgroundColor: "rgb(50,50,50)",
textStyle: { textStyle: {
fontSize: 13, fontSize: 13,
@ -171,6 +176,7 @@ function getOption() {
}, },
xAxis: { xAxis: {
type: "category", type: "category",
show: props.config.showXAxis,
axisTick: { axisTick: {
lineStyle: { color: "#c1c5ca41" }, lineStyle: { color: "#c1c5ca41" },
alignWithLabel: true, alignWithLabel: true,
@ -184,7 +190,11 @@ function getOption() {
axisLine: { show: false }, axisLine: { show: false },
axisTick: { show: false }, axisTick: { show: false },
splitLine: { lineStyle: { color: "#c1c5ca41", type: "dashed" } }, splitLine: { lineStyle: { color: "#c1c5ca41", type: "dashed" } },
axisLabel: { color: "#9da5b2", fontSize: "11" }, axisLabel: {
color: "#9da5b2",
fontSize: "11",
show: props.config.showYAxis,
},
}, },
series: temp, series: temp,
}; };

View File