You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2022/10/31 02:27:42 UTC

[skywalking-booster-ui] branch main updated: feat: support labeled value on the service/instance/endpoint list widgets (#177)

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-booster-ui.git


The following commit(s) were added to refs/heads/main by this push:
     new 09051e9  feat: support labeled value on the service/instance/endpoint list widgets (#177)
09051e9 is described below

commit 09051e916bb20233e0620e185a13e9aa1ef82dd1
Author: Fine0830 <fa...@gmail.com>
AuthorDate: Mon Oct 31 10:27:37 2022 +0800

    feat: support labeled value on the service/instance/endpoint list widgets (#177)
---
 src/hooks/useListConfig.ts                         | 16 ++--
 src/hooks/useProcessor.ts                          | 88 +++++++++++++++++++---
 src/types/dashboard.d.ts                           |  1 +
 .../configuration/widget/metric/Index.vue          | 16 ++--
 .../configuration/widget/metric/Standard.vue       |  4 +-
 src/views/dashboard/controls/Widget.vue            |  2 +-
 src/views/dashboard/graphs/EndpointList.vue        | 55 ++++++++------
 src/views/dashboard/graphs/InstanceList.vue        | 58 ++++++++------
 src/views/dashboard/graphs/Line.vue                |  6 +-
 src/views/dashboard/graphs/ServiceList.vue         | 71 ++++++++++-------
 .../dashboard/graphs/components/ColumnGraph.vue    | 26 +++++--
 11 files changed, 235 insertions(+), 108 deletions(-)

diff --git a/src/hooks/useListConfig.ts b/src/hooks/useListConfig.ts
index a7f7ad5..c87fd39 100644
--- a/src/hooks/useListConfig.ts
+++ b/src/hooks/useListConfig.ts
@@ -26,14 +26,18 @@ export function useListConfig(config: any, index: string) {
     config.metricConfig &&
     config.metricConfig[i] &&
     config.metricConfig[i].calculation;
-  const line =
-    config.metricTypes[i] === MetricQueryTypes.ReadMetricsValues &&
-    !types.includes(calculation);
+  const isLinear =
+    [
+      MetricQueryTypes.ReadMetricsValues,
+      MetricQueryTypes.ReadLabeledMetricsValues,
+    ].includes(config.metricTypes[i]) && !types.includes(calculation);
   const isAvg =
-    config.metricTypes[i] === MetricQueryTypes.ReadMetricsValues &&
-    types.includes(calculation);
+    [
+      MetricQueryTypes.ReadMetricsValues,
+      MetricQueryTypes.ReadLabeledMetricsValues,
+    ].includes(config.metricTypes[i]) && types.includes(calculation);
   return {
-    isLinear: line,
+    isLinear,
     isAvg,
   };
 }
diff --git a/src/hooks/useProcessor.ts b/src/hooks/useProcessor.ts
index d730189..97b22e4 100644
--- a/src/hooks/useProcessor.ts
+++ b/src/hooks/useProcessor.ts
@@ -154,6 +154,7 @@ export function useQueryProcessor(config: any) {
     }
   });
   const queryStr = `query queryData(${variables}) {${fragment}}`;
+
   return {
     queryStr,
     conditions,
@@ -254,13 +255,19 @@ export function useSourceProcessor(
 
 export function useQueryPodsMetrics(
   pods: Array<Instance | Endpoint | Service | any>,
-  config: { metrics: string[]; metricTypes: string[] },
+  config: {
+    metrics: string[];
+    metricTypes: string[];
+    metricConfig: MetricConfigOpt[];
+  },
   scope: string
 ) {
-  if (!(config.metrics && config.metrics[0])) {
+  const metricTypes = (config.metricTypes || []).filter((m: string) => m);
+  if (!metricTypes.length) {
     return;
   }
-  if (!(config.metricTypes && config.metricTypes[0])) {
+  const metrics = (config.metrics || []).filter((m: string) => m);
+  if (!metrics.length) {
     return;
   }
   const appStore = useAppStoreWithOut();
@@ -282,14 +289,24 @@ export function useQueryPodsMetrics(
         endpointName: scope === "Endpoint" ? d.label : undefined,
         normal: scope === "Service" ? d.normal : currentService.normal,
       };
-      const f = config.metrics.map((name: string, idx: number) => {
-        const metricType = config.metricTypes[idx] || "";
+      const f = metrics.map((name: string, idx: number) => {
+        const metricType = metricTypes[idx] || "";
+        variables.push(`$condition${index}${idx}: MetricsCondition!`);
         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]}`;
+        let labelStr = "";
+        if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
+          const c = config.metricConfig[idx] || {};
+          variables.push(`$labels${index}${idx}: [String!]!`);
+          labelStr = `labels: $labels${index}${idx}, `;
+          const labels = (c.labelsIndex || "")
+            .split(",")
+            .map((item: string) => item.replace(/^\s*|\s*$/g, ""));
+          conditions[`labels${index}${idx}`] = labels;
+        }
+        return `${name}${index}${idx}: ${metricType}(condition: $condition${index}${idx}, ${labelStr}duration: $duration)${RespFields[metricType]}`;
       });
       return f;
     }
@@ -299,6 +316,7 @@ export function useQueryPodsMetrics(
 
   return { queryStr, conditions };
 }
+
 export function usePodsSource(
   pods: Array<Instance | Endpoint>,
   resp: { errors: string; data: { [key: string]: any } },
@@ -312,12 +330,20 @@ export function usePodsSource(
     ElMessage.error(resp.errors);
     return {};
   }
+  const names: string[] = [];
+  const metricConfigArr: MetricConfigOpt[] = [];
+  const metricTypesArr: string[] = [];
   const data = pods.map((d: Instance | any, idx: number) => {
     config.metrics.map((name: string, index: number) => {
       const c: any = (config.metricConfig && config.metricConfig[index]) || {};
       const key = name + idx + index;
       if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValue) {
         d[name] = aggregation(resp.data[key], c);
+        if (idx === 0) {
+          names.push(name);
+          metricConfigArr.push(c);
+          metricTypesArr.push(config.metricTypes[index]);
+        }
       }
       if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValues) {
         d[name] = {};
@@ -333,12 +359,56 @@ export function usePodsSource(
         d[name]["values"] = resp.data[key].values.values.map(
           (val: { value: number }) => aggregation(val.value, c)
         );
+        if (idx === 0) {
+          names.push(name);
+          metricConfigArr.push(c);
+          metricTypesArr.push(config.metricTypes[index]);
+        }
+      }
+      if (
+        config.metricTypes[index] === MetricQueryTypes.ReadLabeledMetricsValues
+      ) {
+        const resVal = resp.data[key] || [];
+        const labels = (c.label || "")
+          .split(",")
+          .map((item: string) => item.replace(/^\s*|\s*$/g, ""));
+        const labelsIdx = (c.labelsIndex || "")
+          .split(",")
+          .map((item: string) => item.replace(/^\s*|\s*$/g, ""));
+        for (let i = 0; i < resVal.length; i++) {
+          const item = resVal[i];
+          const values = item.values.values.map((d: { value: number }) =>
+            aggregation(Number(d.value), c)
+          );
+          const indexNum = labelsIdx.findIndex((d: string) => d === item.label);
+          let key = item.label;
+          if (labels[indexNum] && indexNum > -1) {
+            key = labels[indexNum];
+          }
+          if (!d[key]) {
+            d[key] = {};
+          }
+          if (
+            [
+              Calculations.Average,
+              Calculations.ApdexAvg,
+              Calculations.PercentageAvg,
+            ].includes(c.calculation)
+          ) {
+            d[key]["avg"] = calculateExp(item.values.values, c);
+          }
+          d[key]["values"] = values;
+          if (idx === 0) {
+            names.push(key);
+            metricConfigArr.push({ ...c, index: i });
+            metricTypesArr.push(config.metricTypes[index]);
+          }
+        }
       }
     });
-
     return d;
   });
-  return data;
+  return { data, names, metricConfigArr, metricTypesArr };
 }
 export function useQueryTopologyMetrics(metrics: string[], ids: string[]) {
   const appStore = useAppStoreWithOut();
diff --git a/src/types/dashboard.d.ts b/src/types/dashboard.d.ts
index b82afe9..ceabab3 100644
--- a/src/types/dashboard.d.ts
+++ b/src/types/dashboard.d.ts
@@ -74,6 +74,7 @@ export type MetricConfigOpt = {
   labelsIndex: string;
   sortOrder: string;
   topN?: number;
+  index?: number;
 };
 
 export interface WidgetConfig {
diff --git a/src/views/dashboard/configuration/widget/metric/Index.vue b/src/views/dashboard/configuration/widget/metric/Index.vue
index 233eb8a..cdb31f0 100644
--- a/src/views/dashboard/configuration/widget/metric/Index.vue
+++ b/src/views/dashboard/configuration/widget/metric/Index.vue
@@ -191,7 +191,10 @@ async function setMetricType(chart?: any) {
   states.metricList = (arr || []).filter(
     (d: { catalog: string; type: string }) => {
       if (states.isList) {
-        if (d.type === MetricsType.REGULAR_VALUE) {
+        if (
+          d.type === MetricsType.REGULAR_VALUE ||
+          d.type === MetricsType.LABELED_VALUE
+        ) {
           return d;
         }
       } else if (g.type === "Table") {
@@ -239,7 +242,10 @@ async function setMetricType(chart?: any) {
 }
 
 function setDashboards(type?: string) {
-  const chart = type || dashboardStore.selectedGrid.graph || {};
+  const chart =
+    type ||
+    (dashboardStore.selectedGrid.graph &&
+      dashboardStore.selectedGrid.graph.type);
   const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
   const arr = list.reduce(
     (
@@ -248,9 +254,9 @@ function setDashboards(type?: string) {
     ) => {
       if (d.layer === dashboardStore.layerId) {
         if (
-          (d.entity === EntityType[0].value && chart.type === "ServiceList") ||
-          (d.entity === EntityType[2].value && chart.type === "EndpointList") ||
-          (d.entity === EntityType[3].value && chart.type === "InstanceList")
+          (d.entity === EntityType[0].value && chart === "ServiceList") ||
+          (d.entity === EntityType[2].value && chart === "EndpointList") ||
+          (d.entity === EntityType[3].value && chart === "InstanceList")
         ) {
           prev.push({
             ...d,
diff --git a/src/views/dashboard/configuration/widget/metric/Standard.vue b/src/views/dashboard/configuration/widget/metric/Standard.vue
index 150c220..1ba282c 100644
--- a/src/views/dashboard/configuration/widget/metric/Standard.vue
+++ b/src/views/dashboard/configuration/widget/metric/Standard.vue
@@ -115,7 +115,9 @@ const currentMetric = ref<MetricConfigOpt>({
   topN: props.currentMetricConfig.topN || 10,
 });
 const metricTypes = dashboardStore.selectedGrid.metricTypes || [];
-const metricType = ref<string>(metricTypes[props.index]);
+const metricType = computed(
+  () => (dashboardStore.selectedGrid.metricTypes || [])[props.index]
+);
 const hasLabel = computed(() => {
   const graph = dashboardStore.selectedGrid.graph || {};
   return (
diff --git a/src/views/dashboard/controls/Widget.vue b/src/views/dashboard/controls/Widget.vue
index f723984..8593d43 100644
--- a/src/views/dashboard/controls/Widget.vue
+++ b/src/views/dashboard/controls/Widget.vue
@@ -62,7 +62,7 @@ limitations under the License. -->
           metricTypes: data.metricTypes || [''],
           i: data.i,
           id: data.id,
-          metricConfig: data.metricConfig,
+          metricConfig: data.metricConfig || [],
           filters: data.filters || {},
           relatedTrace: data.relatedTrace || {},
         }"
diff --git a/src/views/dashboard/graphs/EndpointList.vue b/src/views/dashboard/graphs/EndpointList.vue
index c4654b2..138865c 100644
--- a/src/views/dashboard/graphs/EndpointList.vue
+++ b/src/views/dashboard/graphs/EndpointList.vue
@@ -31,7 +31,7 @@ limitations under the License. -->
     </div>
     <div class="list">
       <el-table v-loading="chartLoading" :data="endpoints" style="width: 100%">
-        <el-table-column label="Endpoints">
+        <el-table-column label="Endpoints" fixed min-width="220">
           <template #default="scope">
             <span
               class="link"
@@ -45,7 +45,12 @@ limitations under the License. -->
         <ColumnGraph
           :intervalTime="intervalTime"
           :colMetrics="colMetrics"
-          :config="config"
+          :config="{
+            ...config,
+            metrics: colMetrics,
+            metricConfig,
+            metricTypes,
+          }"
           v-if="colMetrics.length"
         />
       </el-table>
@@ -53,7 +58,7 @@ limitations under the License. -->
   </div>
 </template>
 <script setup lang="ts">
-import { ref, watch, computed } from "vue";
+import { ref, watch } from "vue";
 import { useSelectorStore } from "@/store/modules/selectors";
 import { ElMessage } from "element-plus";
 import { useI18n } from "vue-i18n";
@@ -99,9 +104,9 @@ const dashboardStore = useDashboardStore();
 const chartLoading = ref<boolean>(false);
 const endpoints = ref<Endpoint[]>([]);
 const searchText = ref<string>("");
-const colMetrics = computed(() =>
-  (props.config.metrics || []).filter((d: string) => d)
-);
+const colMetrics = ref<string[]>([]);
+const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
+const metricTypes = ref<string[]>(props.config.metricTypes || []);
 
 if (props.needQuery) {
   queryEndpoints();
@@ -125,8 +130,8 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
     return;
   }
   const metrics = props.config.metrics || [];
-  const metricTypes = props.config.metricTypes || [];
-  if (metrics.length && metrics[0] && metricTypes.length && metricTypes[0]) {
+  const types = props.config.metricTypes || [];
+  if (metrics.length && metrics[0] && types.length && types[0]) {
     const params = await useQueryPodsMetrics(
       currentPods,
       props.config,
@@ -138,12 +143,18 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
       ElMessage.error(json.errors);
       return;
     }
-    const metricConfig = props.config.metricConfig || [];
-
-    endpoints.value = usePodsSource(currentPods, json, {
-      ...props.config,
-      metricConfig: metricConfig,
-    });
+    const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(
+      currentPods,
+      json,
+      {
+        ...props.config,
+        metricConfig: metricConfig.value,
+      }
+    );
+    endpoints.value = data;
+    colMetrics.value = names;
+    metricTypes.value = metricTypesArr;
+    metricConfig.value = metricConfigArr;
     return;
   }
   endpoints.value = currentPods;
@@ -166,11 +177,16 @@ async function searchList() {
   await queryEndpoints();
 }
 watch(
-  () => [...(props.config.metricTypes || []), ...(props.config.metrics || [])],
+  () => [
+    ...(props.config.metricTypes || []),
+    ...(props.config.metrics || []),
+    ...(props.config.metricConfig || []),
+  ],
   (data, old) => {
     if (JSON.stringify(data) === JSON.stringify(old)) {
       return;
     }
+    metricConfig.value = props.config.metricConfig;
     queryEndpointMetrics(endpoints.value);
   }
 );
@@ -180,15 +196,6 @@ watch(
     queryEndpoints();
   }
 );
-watch(
-  () => [...(props.config.metricConfig || [])],
-  (data, old) => {
-    if (JSON.stringify(data) === JSON.stringify(old)) {
-      return;
-    }
-    queryEndpointMetrics(endpoints.value);
-  }
-);
 </script>
 <style lang="scss" scoped>
 @import "./style.scss";
diff --git a/src/views/dashboard/graphs/InstanceList.vue b/src/views/dashboard/graphs/InstanceList.vue
index 04ae541..2963102 100644
--- a/src/views/dashboard/graphs/InstanceList.vue
+++ b/src/views/dashboard/graphs/InstanceList.vue
@@ -30,7 +30,7 @@ limitations under the License. -->
     </div>
     <div class="list">
       <el-table v-loading="chartLoading" :data="instances" style="width: 100%">
-        <el-table-column label="Service Instances">
+        <el-table-column label="Service Instances" fixed min-width="320">
           <template #default="scope">
             <span
               class="link"
@@ -42,12 +42,17 @@ limitations under the License. -->
           </template>
         </el-table-column>
         <ColumnGraph
-          v-if="colMetrics.length"
           :intervalTime="intervalTime"
           :colMetrics="colMetrics"
-          :config="config"
+          :config="{
+            ...config,
+            metrics: colMetrics,
+            metricConfig,
+            metricTypes,
+          }"
+          v-if="colMetrics.length"
         />
-        <el-table-column label="Attributes">
+        <el-table-column label="Attributes" fixed="right" min-width="100">
           <template #default="scope">
             <el-popover placement="left" :width="400" trigger="click">
               <template #reference>
@@ -82,7 +87,7 @@ limitations under the License. -->
   </div>
 </template>
 <script setup lang="ts">
-import { ref, watch, computed } from "vue";
+import { ref, watch } from "vue";
 import { useI18n } from "vue-i18n";
 import { ElMessage } from "element-plus";
 import type { PropType } from "vue";
@@ -126,9 +131,9 @@ const chartLoading = ref<boolean>(false);
 const instances = ref<Instance[]>([]); // current instances
 const pageSize = 10;
 const searchText = ref<string>("");
-const colMetrics = computed(() =>
-  (props.config.metrics || []).filter((d: string) => d)
-);
+const colMetrics = ref<string[]>([]);
+const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
+const metricTypes = ref<string[]>(props.config.metricTypes || []);
 if (props.needQuery) {
   queryInstance();
 }
@@ -154,9 +159,9 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
     return;
   }
   const metrics = props.config.metrics || [];
-  const metricTypes = props.config.metricTypes || [];
+  const types = props.config.metricTypes || [];
 
-  if (metrics.length && metrics[0] && metricTypes.length && metricTypes[0]) {
+  if (metrics.length && metrics[0] && types.length && types[0]) {
     const params = await useQueryPodsMetrics(
       currentInstances,
       props.config,
@@ -168,11 +173,18 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
       ElMessage.error(json.errors);
       return;
     }
-    const metricConfig = props.config.metricConfig || [];
-    instances.value = usePodsSource(currentInstances, json, {
-      ...props.config,
-      metricConfig,
-    });
+    const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(
+      currentInstances,
+      json,
+      {
+        ...props.config,
+        metricConfig: metricConfig.value,
+      }
+    );
+    instances.value = data;
+    colMetrics.value = names;
+    metricTypes.value = metricTypesArr;
+    metricConfig.value = metricConfigArr;
     return;
   }
   instances.value = currentInstances;
@@ -215,11 +227,16 @@ function searchList() {
 }
 
 watch(
-  () => [...(props.config.metricTypes || []), ...(props.config.metrics || [])],
+  () => [
+    ...(props.config.metricTypes || []),
+    ...(props.config.metrics || []),
+    ...(props.config.metricConfig || []),
+  ],
   (data, old) => {
     if (JSON.stringify(data) === JSON.stringify(old)) {
       return;
     }
+    metricConfig.value = props.config.metricConfig;
     queryInstanceMetrics(instances.value);
   }
 );
@@ -229,15 +246,6 @@ watch(
     queryInstance();
   }
 );
-watch(
-  () => [...(props.config.metricConfig || [])],
-  (data, old) => {
-    if (JSON.stringify(data) === JSON.stringify(old)) {
-      return;
-    }
-    queryInstanceMetrics(instances.value);
-  }
-);
 </script>
 <style lang="scss" scoped>
 @import "./style.scss";
diff --git a/src/views/dashboard/graphs/Line.vue b/src/views/dashboard/graphs/Line.vue
index d1c3d82..ae27ff7 100644
--- a/src/views/dashboard/graphs/Line.vue
+++ b/src/views/dashboard/graphs/Line.vue
@@ -42,9 +42,9 @@ const props = defineProps({
   config: {
     type: Object as PropType<
       LineConfig & {
-        filters: Filters;
-        relatedTrace: RelatedTrace;
-      } & { id: string }
+        filters?: Filters;
+        relatedTrace?: RelatedTrace;
+      } & { id?: string }
     >,
     default: () => ({
       step: false,
diff --git a/src/views/dashboard/graphs/ServiceList.vue b/src/views/dashboard/graphs/ServiceList.vue
index a08d22c..3a59762 100644
--- a/src/views/dashboard/graphs/ServiceList.vue
+++ b/src/views/dashboard/graphs/ServiceList.vue
@@ -37,12 +37,17 @@ limitations under the License. -->
         :border="true"
         :style="{ fontSize: '14px' }"
       >
-        <el-table-column label="Service Groups" v-if="config.showGroup">
+        <el-table-column
+          fixed
+          label="Service Groups"
+          v-if="config.showGroup"
+          min-width="150"
+        >
           <template #default="scope">
             {{ scope.row.group }}
           </template>
         </el-table-column>
-        <el-table-column label="Service Names">
+        <el-table-column fixed label="Service Names" min-width="220">
           <template #default="scope">
             <span
               class="link"
@@ -56,7 +61,12 @@ limitations under the License. -->
         <ColumnGraph
           :intervalTime="intervalTime"
           :colMetrics="colMetrics"
-          :config="config"
+          :config="{
+            ...config,
+            metrics: colMetrics,
+            metricConfig,
+            metricTypes,
+          }"
           v-if="colMetrics.length"
         />
       </el-table>
@@ -75,7 +85,7 @@ limitations under the License. -->
   </div>
 </template>
 <script setup lang="ts">
-import { watch, ref, computed } from "vue";
+import { watch, ref } from "vue";
 import { ElMessage } from "element-plus";
 import type { PropType } from "vue";
 import { ServiceListConfig } from "@/types/dashboard";
@@ -102,7 +112,9 @@ const props = defineProps({
         metrics: string[];
         metricTypes: string[];
         isEdit: boolean;
-      } & { metricConfig: MetricConfigOpt[] }
+        names: string[];
+        metricConfig: MetricConfigOpt[];
+      }
     >,
     default: () => ({ dashboardName: "", fontSize: 12 }),
   },
@@ -115,12 +127,13 @@ const appStore = useAppStoreWithOut();
 const chartLoading = ref<boolean>(false);
 const pageSize = 10;
 const services = ref<Service[]>([]);
+const colMetrics = ref<string[]>([]);
 const searchText = ref<string>("");
 const groups = ref<any>({});
 const sortServices = ref<(Service & { merge: boolean })[]>([]);
-const colMetrics = computed(() =>
-  (props.config.metrics || []).filter((d: string) => d)
-);
+const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
+const metricTypes = ref<string[]>(props.config.metricTypes || []);
+
 queryServices();
 
 async function queryServices() {
@@ -198,12 +211,12 @@ async function queryServiceMetrics(currentServices: Service[]) {
     return;
   }
   const metrics = props.config.metrics || [];
-  const metricTypes = props.config.metricTypes || [];
+  const types = props.config.metricTypes || [];
 
-  if (metrics.length && metrics[0] && metricTypes.length && metricTypes[0]) {
+  if (metrics.length && metrics[0] && types.length && types[0]) {
     const params = await useQueryPodsMetrics(
       currentServices,
-      props.config,
+      { ...props.config, metricConfig: metricConfig.value || [] },
       EntityType[0].value
     );
     const json = await dashboardStore.fetchMetricValue(params);
@@ -212,14 +225,22 @@ async function queryServiceMetrics(currentServices: Service[]) {
       ElMessage.error(json.errors);
       return;
     }
-    const metricConfig = props.config.metricConfig || [];
-    services.value = usePodsSource(currentServices, json, {
-      ...props.config,
-      metricConfig: metricConfig || [],
-    });
+
+    const { data, names, metricConfigArr, metricTypesArr } = usePodsSource(
+      currentServices,
+      json,
+      {
+        ...props.config,
+        metricConfig: metricConfig.value || [],
+      }
+    );
+    services.value = data;
+    colMetrics.value = names;
+    metricTypes.value = metricTypesArr;
+    metricConfig.value = metricConfigArr;
+
     return;
   }
-
   services.value = currentServices;
 }
 function objectSpanMethod(param: any): any {
@@ -257,20 +278,16 @@ function searchList() {
 }
 
 watch(
-  () => [...(props.config.metricTypes || []), ...(props.config.metrics || [])],
-  (data, old) => {
-    if (JSON.stringify(data) === JSON.stringify(old)) {
-      return;
-    }
-    queryServiceMetrics(services.value);
-  }
-);
-watch(
-  () => [...(props.config.metricConfig || [])],
+  () => [
+    ...(props.config.metricTypes || []),
+    ...(props.config.metrics || []),
+    ...(props.config.metricConfig || []),
+  ],
   (data, old) => {
     if (JSON.stringify(data) === JSON.stringify(old)) {
       return;
     }
+    metricConfig.value = props.config.metricConfig;
     queryServiceMetrics(services.value);
   }
 );
diff --git a/src/views/dashboard/graphs/components/ColumnGraph.vue b/src/views/dashboard/graphs/components/ColumnGraph.vue
index c5086e5..d57bfbe 100644
--- a/src/views/dashboard/graphs/components/ColumnGraph.vue
+++ b/src/views/dashboard/graphs/components/ColumnGraph.vue
@@ -20,6 +20,7 @@ limitations under the License. -->
       getLabel(metric, index)
     )} ${decodeURIComponent(getUnit(index))}`"
     :key="metric + index"
+    min-width="150"
   >
     <template #default="scope">
       <div class="chart">
@@ -90,18 +91,18 @@ import { MetricConfigOpt } from "@/types/dashboard";
 import { useListConfig } from "@/hooks/useListConfig";
 import Line from "../Line.vue";
 import Card from "../Card.vue";
+import { MetricQueryTypes } from "@/hooks/data";
 
 /*global defineProps */
 const props = defineProps({
   colMetrics: { type: Object },
   config: {
-    type: Object as PropType<
-      {
-        i: string;
-        metrics: string[];
-        metricTypes: string[];
-      } & { metricConfig: MetricConfigOpt[] }
-    >,
+    type: Object as PropType<{
+      i: string;
+      metrics: string[];
+      metricTypes: string[];
+      metricConfig: MetricConfigOpt[];
+    }>,
     default: () => ({}),
   },
   intervalTime: { type: Array as PropType<string[]>, default: () => [] },
@@ -125,6 +126,16 @@ function getLabel(metric: string, index: string) {
     props.config.metricConfig[i] &&
     props.config.metricConfig[i].label;
   if (label) {
+    if (
+      props.config.metricTypes[i] === MetricQueryTypes.ReadLabeledMetricsValues
+    ) {
+      const name = (label || "")
+        .split(",")
+        .map((item: string) => item.replace(/^\s*|\s*$/g, ""))[
+        props.config.metricConfig[i].index || 0
+      ];
+      return encodeURIComponent(name || "");
+    }
     return encodeURIComponent(label);
   }
   return encodeURIComponent(metric);
@@ -157,5 +168,6 @@ function getLabel(metric: string, index: string) {
   display: inline-block;
   flex-grow: 2;
   height: 100%;
+  width: calc(100% - 30px);
 }
 </style>