mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-02 07:14:05 +00:00
feat: visualize metrics in InstanceList
This commit is contained in:
parent
e26380c359
commit
0f01ac34a9
@ -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;
|
||||||
|
}
|
||||||
|
@ -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 } },
|
||||||
|
@ -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 {
|
||||||
|
9
src/types/selector.d.ts
vendored
9
src/types/selector.d.ts
vendored
@ -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 }[];
|
||||||
|
};
|
||||||
|
@ -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>
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
0
src/views/dashboard/graphs/MinLine.vue
Normal file
0
src/views/dashboard/graphs/MinLine.vue
Normal file
Loading…
Reference in New Issue
Block a user