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 { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app";
import { Instance } from "@/types/selector";
export function useQueryProcessor(config: any) {
if (!(config.metrics && config.metrics.length)) {
@ -191,3 +192,61 @@ function aggregation(json: {
}
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 axios, { AxiosResponse } from "axios";
import { cancelToken } from "@/utils/cancelToken";
import { Instance } from "@/types/selector";
interface DashboardState {
showConfig: boolean;
layout: LayoutConfig[];
@ -179,7 +180,10 @@ export const dashboardStore = defineStore({
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(
"/graphql",
{ query: param.queryStr, variables: { ...param.conditions } },

View File

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

View File

@ -22,3 +22,12 @@ export type Service = {
normal: boolean;
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">
<component
:is="`${states.graph.type}Config`"
:config="states.graph"
:config="{
...states.graph,
metrics: states.metrics,
metricTypes: states.metricTypes,
}"
@update="updateGraphOptions"
/>
</el-collapse-item>

View File

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

View File

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

View File

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

View File

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

View File