refactor: remove the General metric mode and related logical code (#384)

This commit is contained in:
Fine0830 2024-04-11 17:50:43 +08:00 committed by GitHub
parent 460b24f42c
commit 03f321b62a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 204 additions and 1790 deletions

View File

@ -14,32 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
export enum MetricQueryTypes {
ReadMetricsValue = "readMetricsValue",
ReadMetricsValues = "readMetricsValues",
SortMetrics = "sortMetrics",
ReadLabeledMetricsValues = "readLabeledMetricsValues",
READHEATMAP = "readHeatMap",
ReadSampledRecords = "readSampledRecords",
ReadRecords = "readRecords",
ReadNullableMetricsValue = "readNullableMetricsValue",
}
export enum Calculations {
Percentage = "percentage",
ByteToKB = "byteToKB",
ByteToMB = "byteToMB",
ByteToGB = "byteToGB",
Apdex = "apdex",
ConvertSeconds = "convertSeconds",
ConvertMilliseconds = "convertMilliseconds",
MsToS = "msTos",
Average = "average",
PercentageAvg = "percentageAvg",
ApdexAvg = "apdexAvg",
SecondToDay = "secondToDay",
NanosecondToMillisecond = "nanosecondToMillisecond",
}
export enum sizeEnum { export enum sizeEnum {
XS = "XS", XS = "XS",
SM = "SM", SM = "SM",
@ -68,50 +43,6 @@ screenMap.set(sizeEnum.XL, screenEnum.XL);
screenMap.set(sizeEnum.XXL, screenEnum.XXL); screenMap.set(sizeEnum.XXL, screenEnum.XXL);
export const RespFields: Indexable = { export const RespFields: Indexable = {
readMetricsValues: `{
label
values {
values {value isEmptyValue}
}
}`,
readMetricsValue: ``,
readNullableMetricsValue: `{
value
isEmptyValue
}`,
sortMetrics: `{
name
id
value
refId
}`,
readLabeledMetricsValues: `{
label
values {
values {value isEmptyValue}
}
}`,
readHeatMap: `{
values {
id
values
}
buckets {
min
max
}
}`,
readSampledRecords: `{
name
value
refId
}`,
readRecords: `{
id
name
value
refId
}`,
execExpression: `{ execExpression: `{
type type
results { results {

View File

@ -129,9 +129,9 @@ export async function useExpressionsQueryProcessor(config: Indexable) {
} }
if (type === ExpressionResultType.SINGLE_VALUE) { if (type === ExpressionResultType.SINGLE_VALUE) {
for (const item of results) { for (const item of results) {
const label = item.metric.labels const label =
.map((d: { key: string; value: string }) => `${d.key}=${d.value}`) item.metric &&
.join(","); item.metric.labels.map((d: { key: string; value: string }) => `${d.key}=${d.value}`).join(",");
const values = item.values.map((d: { value: unknown }) => d.value) || []; const values = item.values.map((d: { value: unknown }) => d.value) || [];
if (results.length === 1) { if (results.length === 1) {
source[label || c.label || name] = values; source[label || c.label || name] = values;

View File

@ -1,41 +0,0 @@
/**
* 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.
*/
import { MetricQueryTypes, Calculations } from "./data";
import { MetricModes } from "@/views/dashboard/data";
export function useListConfig(config: Indexable, index: number) {
if (config.metricModes === MetricModes.Expression) {
return {
isLinear: false,
isAvg: true,
};
}
const i = Number(index);
const types = [Calculations.Average, Calculations.ApdexAvg, Calculations.PercentageAvg];
const calculation = config.metricConfig && config.metricConfig[i] && config.metricConfig[i].calculation;
const isLinear =
[MetricQueryTypes.ReadMetricsValues, MetricQueryTypes.ReadLabeledMetricsValues].includes(config.metricTypes[i]) &&
!types.includes(calculation);
const isAvg =
[MetricQueryTypes.ReadMetricsValues, MetricQueryTypes.ReadLabeledMetricsValues].includes(config.metricTypes[i]) &&
types.includes(calculation);
return {
isLinear,
isAvg,
};
}

View File

@ -1,437 +0,0 @@
/**
* 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.
*/
import dayjs from "dayjs";
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 type { Instance, Endpoint, Service } from "@/types/selector";
import type { MetricConfigOpt } from "@/types/dashboard";
export function useQueryProcessor(config: Indexable) {
if (!(config.metrics && config.metrics[0])) {
return;
}
if (!(config.metricTypes && config.metricTypes[0])) {
return;
}
const appStore = useAppStoreWithOut();
const dashboardStore = useDashboardStore();
const selectorStore = useSelectorStore();
if (!selectorStore.currentService && dashboardStore.entity !== "All") {
return;
}
const conditions: Recordable = {
duration: appStore.durationTime,
};
const variables: string[] = [`$duration: Duration!`];
const isRelation = ["ServiceRelation", "ServiceInstanceRelation", "EndpointRelation", "ProcessRelation"].includes(
dashboardStore.entity,
);
if (isRelation && !selectorStore.currentDestService) {
return;
}
const fragment = config.metrics.map((name: string, index: number) => {
const metricType = config.metricTypes[index] || "";
const c = (config.metricConfig && config.metricConfig[index]) || {};
if ([MetricQueryTypes.ReadSampledRecords, MetricQueryTypes.SortMetrics].includes(metricType)) {
variables.push(`$condition${index}: TopNCondition!`);
conditions[`condition${index}`] = {
name,
parentService: ["All"].includes(dashboardStore.entity) ? null : selectorStore.currentService.value,
normal: selectorStore.currentService ? selectorStore.currentService.normal : true,
topN: Number(c.topN) || 10,
order: c.sortOrder || "DES",
};
} else {
const entity = {
serviceName: dashboardStore.entity === "All" ? undefined : selectorStore.currentService.value,
normal: dashboardStore.entity === "All" ? undefined : selectorStore.currentService.normal,
serviceInstanceName: ["ServiceInstance", "ServiceInstanceRelation", "ProcessRelation"].includes(
dashboardStore.entity,
)
? selectorStore.currentPod && selectorStore.currentPod.value
: undefined,
endpointName: dashboardStore.entity.includes("Endpoint")
? selectorStore.currentPod && selectorStore.currentPod.value
: undefined,
processName: dashboardStore.entity.includes("Process")
? selectorStore.currentProcess && selectorStore.currentProcess.value
: undefined,
destNormal: isRelation ? selectorStore.currentDestService.normal : undefined,
destServiceName: isRelation ? selectorStore.currentDestService.value : undefined,
destServiceInstanceName: ["ServiceInstanceRelation", "ProcessRelation"].includes(dashboardStore.entity)
? selectorStore.currentDestPod && selectorStore.currentDestPod.value
: undefined,
destEndpointName:
dashboardStore.entity === "EndpointRelation"
? selectorStore.currentDestPod && selectorStore.currentDestPod.value
: undefined,
destProcessName: dashboardStore.entity.includes("ProcessRelation")
? selectorStore.currentDestProcess && selectorStore.currentDestProcess.value
: undefined,
};
if ([MetricQueryTypes.ReadRecords].includes(metricType)) {
variables.push(`$condition${index}: RecordCondition!`);
conditions[`condition${index}`] = {
name,
parentEntity: entity,
topN: Number(c.topN) || 10,
order: c.sortOrder || "DES",
};
} else {
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
const labels = (c.labelsIndex || "").split(",").map((item: string) => item.replace(/^\s*|\s*$/g, ""));
variables.push(`$labels${index}: [String!]!`);
conditions[`labels${index}`] = labels;
}
variables.push(`$condition${index}: MetricsCondition!`);
conditions[`condition${index}`] = {
name,
entity,
};
}
}
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
return `${name}${index}: ${metricType}(condition: $condition${index}, labels: $labels${index}, duration: $duration)${RespFields[metricType]}`;
}
const t = metricType === MetricQueryTypes.ReadMetricsValue ? MetricQueryTypes.ReadNullableMetricsValue : metricType;
return `${name}${index}: ${t}(condition: $condition${index}, duration: $duration)${RespFields[t]}`;
});
const queryStr = `query queryData(${variables}) {${fragment}}`;
return {
queryStr,
conditions,
};
}
export function useSourceProcessor(
resp: { errors: string; data: Indexable },
config: {
metrics: string[];
metricTypes: string[];
metricConfig: MetricConfigOpt[];
},
) {
if (resp.errors) {
ElMessage.error(resp.errors);
return {};
}
if (!resp.data) {
ElMessage.error("The query is wrong");
return {};
}
const source: { [key: string]: unknown } = {};
const keys = Object.keys(resp.data);
config.metricTypes.forEach((type: string, index) => {
const m = config.metrics[index];
const c = (config.metricConfig && config.metricConfig[index]) || {};
if (type === MetricQueryTypes.ReadMetricsValues) {
source[c.label || m] = (resp.data[keys[index]] && calculateExp(resp.data[keys[index]].values.values, c)) || [];
}
if (type === MetricQueryTypes.ReadLabeledMetricsValues) {
const resVal = Object.values(resp.data)[0] || [];
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 (const item of resVal) {
const values = item.values.values.map((d: { value: number; isEmptyValue: boolean }) =>
d.isEmptyValue ? NaN : aggregation(Number(d.value), c),
);
const indexNum = labelsIdx.findIndex((d: string) => d === item.label);
if (labels[indexNum] && indexNum > -1) {
source[labels[indexNum]] = values;
} else {
source[item.label] = values;
}
}
}
if (type === MetricQueryTypes.ReadMetricsValue) {
const v = Object.values(resp.data)[0] || {};
source[m] = v.isEmptyValue ? NaN : aggregation(Number(v.value), c);
}
if (
(
[MetricQueryTypes.ReadRecords, MetricQueryTypes.ReadSampledRecords, MetricQueryTypes.SortMetrics] as string[]
).includes(type)
) {
source[m] = (Object.values(resp.data)[0] || []).map((d: { value: unknown; name: string }) => {
d.value = aggregation(Number(d.value), c);
return d;
});
}
if (type === MetricQueryTypes.READHEATMAP) {
const resVal = Object.values(resp.data)[0] || {};
const nodes = [] as Indexable[];
if (!(resVal && resVal.values)) {
source[m] = { nodes: [] };
return;
}
resVal.values.forEach((items: { values: number[] }, x: number) => {
const grids = items.values.map((val: number, y: number) => [x, y, val]);
nodes.push(...grids);
});
let buckets = [] as Indexable[];
if (resVal.buckets.length) {
buckets = [resVal.buckets[0].min, ...resVal.buckets.map((item: { min: string; max: string }) => item.max)];
}
source[m] = { nodes, buckets }; // nodes: number[][]
}
});
return source;
}
export function useQueryPodsMetrics(
pods: Array<(Instance | Endpoint | Service) & Indexable>,
config: {
metrics: string[];
metricTypes: string[];
metricConfig: MetricConfigOpt[];
},
scope: string,
) {
const metricTypes = (config.metricTypes || []).filter((m: string) => m);
if (!metricTypes.length) {
return;
}
const metrics = (config.metrics || []).filter((m: string) => m);
if (!metrics.length) {
return;
}
const appStore = useAppStoreWithOut();
const selectorStore = useSelectorStore();
const conditions: { [key: string]: unknown } = {
duration: appStore.durationTime,
};
const variables: string[] = [`$duration: Duration!`];
const currentService = selectorStore.currentService || {};
const fragmentList = pods.map((d: (Instance | Endpoint | Service) & Indexable, index: number) => {
const param = {
serviceName: scope === "Service" ? d.label : currentService.label,
serviceInstanceName: scope === "ServiceInstance" ? d.label : undefined,
endpointName: scope === "Endpoint" ? d.label : undefined,
normal: scope === "Service" ? d.normal : currentService.normal,
};
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,
};
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;
}
const t =
metricType === MetricQueryTypes.ReadMetricsValue ? MetricQueryTypes.ReadNullableMetricsValue : metricType;
return `${name}${index}${idx}: ${t}(condition: $condition${index}${idx}, ${labelStr}duration: $duration)${RespFields[t]}`;
});
return f;
});
const fragment = fragmentList.flat(1).join(" ");
const queryStr = `query queryData(${variables}) {${fragment}}`;
return { queryStr, conditions };
}
export function usePodsSource(
pods: Array<Instance | Endpoint>,
resp: { errors: string; data: Indexable },
config: {
metrics: string[];
metricTypes: string[];
metricConfig: MetricConfigOpt[];
},
): Indexable {
if (resp.errors) {
ElMessage.error(resp.errors);
return {};
}
const names: string[] = [];
const metricConfigArr: MetricConfigOpt[] = [];
const metricTypesArr: string[] = [];
const data = pods.map((d: 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) {
const v = resp.data[key];
d[name] = v.isEmptyValue ? NaN : aggregation(v.value, c);
if (idx === 0) {
names.push(name);
metricConfigArr.push(c);
metricTypesArr.push(config.metricTypes[index]);
}
}
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValues) {
d[name] = {};
if ([Calculations.Average, Calculations.ApdexAvg, Calculations.PercentageAvg].includes(c.calculation)) {
d[name]["avg"] = calculateExp(resp.data[key].values.values, c);
}
d[name]["values"] = resp.data[key].values.values.map((val: { value: number; isEmptyValue: boolean }) =>
val.isEmptyValue ? NaN : 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; isEmptyValue: boolean }) =>
d.isEmptyValue ? NaN : 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, names, metricConfigArr, metricTypesArr };
}
export function useQueryTopologyMetrics(metrics: string[], ids: string[]) {
const appStore = useAppStoreWithOut();
const conditions: { [key: string]: unknown } = {
duration: appStore.durationTime,
ids,
};
const variables: string[] = [`$duration: Duration!`, `$ids: [ID!]!`];
const fragmentList = metrics.map((d: string, index: number) => {
conditions[`m${index}`] = d;
variables.push(`$m${index}: String!`);
return `${d}: getValues(metric: {
name: $m${index}
ids: $ids
}, duration: $duration) {
values {
id
value
}
}`;
});
const queryStr = `query queryData(${variables}) {${fragmentList.join(" ")}}`;
return { queryStr, conditions };
}
export function calculateExp(
list: { value: number; isEmptyValue: boolean }[],
config: { calculation?: string },
): (number | string)[] {
const arr = list.filter((d: { value: number; isEmptyValue: boolean }) => !d.isEmptyValue);
const sum = arr.length ? arr.map((d: { value: number }) => Number(d.value)).reduce((a, b) => a + b) : 0;
let data: (number | string)[] = [];
switch (config.calculation) {
case Calculations.Average:
data = [(sum / arr.length).toFixed(2)];
break;
case Calculations.PercentageAvg:
data = [(sum / arr.length / 100).toFixed(2)];
break;
case Calculations.ApdexAvg:
data = [(sum / arr.length / 10000).toFixed(2)];
break;
default:
data = list.map((d: { value: number; isEmptyValue: boolean }) =>
d.isEmptyValue ? NaN : aggregation(d.value, config),
);
break;
}
return data;
}
export function aggregation(val: number, config: { calculation?: string }): number | string {
let data: number | string = Number(val);
switch (config.calculation) {
case Calculations.Percentage:
data = (val / 100).toFixed(2);
break;
case Calculations.PercentageAvg:
data = (val / 100).toFixed(2);
break;
case Calculations.ByteToKB:
data = (val / 1024).toFixed(2);
break;
case Calculations.ByteToMB:
data = (val / 1024 / 1024).toFixed(2);
break;
case Calculations.ByteToGB:
data = (val / 1024 / 1024 / 1024).toFixed(2);
break;
case Calculations.Apdex:
data = (val / 10000).toFixed(2);
break;
case Calculations.ConvertSeconds:
data = dayjs(val * 1000).format("YYYY-MM-DD HH:mm:ss");
break;
case Calculations.ConvertMilliseconds:
data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
break;
case Calculations.MsToS:
data = (val / 1000).toFixed(2);
break;
case Calculations.SecondToDay:
data = (val / 86400).toFixed(2);
break;
case Calculations.NanosecondToMillisecond:
data = (val / 1000 / 1000).toFixed(2);
break;
case Calculations.ApdexAvg:
data = (val / 10000).toFixed(2);
break;
default:
data;
break;
}
return data;
}

View File

@ -377,7 +377,6 @@ const msg = {
menus: "Menus", menus: "Menus",
saveReload: "Save and reload the page", saveReload: "Save and reload the page",
document: "Documentation", document: "Documentation",
metricMode: "Metric Mode",
addExpressions: "Add Expressions", addExpressions: "Add Expressions",
expressions: "Expression", expressions: "Expression",
unhealthyExpression: "Unhealthy Expression", unhealthyExpression: "Unhealthy Expression",

View File

@ -377,7 +377,6 @@ const msg = {
menus: "Menus", menus: "Menus",
saveReload: "Save and reload the page", saveReload: "Save and reload the page",
document: "Documentation", document: "Documentation",
metricMode: "Metric Mode",
addExpressions: "Add Expressions", addExpressions: "Add Expressions",
expressions: "Expression", expressions: "Expression",
unhealthyExpression: "Unhealthy Expression", unhealthyExpression: "Unhealthy Expression",

View File

@ -375,7 +375,6 @@ const msg = {
menusManagement: "菜单", menusManagement: "菜单",
saveReload: "保存并重新加载页面", saveReload: "保存并重新加载页面",
document: "文档", document: "文档",
metricMode: "指标模式",
addExpressions: "添加表达式", addExpressions: "添加表达式",
expressions: "表达式", expressions: "表达式",
unhealthyExpression: "非健康表达式", unhealthyExpression: "非健康表达式",

View File

@ -24,7 +24,7 @@ import { useSelectorStore } from "@/store/modules/selectors";
import { NewControl, TextConfig, TimeRangeConfig, ControlsTypes } from "../data"; import { NewControl, TextConfig, TimeRangeConfig, ControlsTypes } from "../data";
import type { AxiosResponse } from "axios"; import type { AxiosResponse } from "axios";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { EntityType, MetricModes, WidgetType } from "@/views/dashboard/data"; import { EntityType, WidgetType } from "@/views/dashboard/data";
interface DashboardState { interface DashboardState {
showConfig: boolean; showConfig: boolean;
layout: LayoutConfig[]; layout: LayoutConfig[];
@ -88,13 +88,7 @@ export const dashboardStore = defineStore({
i: index, i: index,
id: index, id: index,
type, type,
metricTypes: [""],
metrics: [""],
}; };
if (type === WidgetType.Widget) {
newItem.metricMode = MetricModes.Expression;
}
if (type === WidgetType.Tab) { if (type === WidgetType.Tab) {
newItem.h = 36; newItem.h = 36;
newItem.activedTabIndex = 0; newItem.activedTabIndex = 0;
@ -167,18 +161,12 @@ export const dashboardStore = defineStore({
i: index, i: index,
id, id,
type, type,
metricTypes: [""],
metrics: [""],
}; };
if (type === WidgetType.Widget) {
newItem.metricMode = MetricModes.Expression;
}
if (type === WidgetType.Topology) { if (type === WidgetType.Topology) {
newItem.h = 32; newItem.h = 32;
newItem.graph = { newItem.graph = {
showDepth: true, showDepth: true,
}; };
newItem.metricMode = MetricModes.Expression;
} }
if (ControlsTypes.includes(type)) { if (ControlsTypes.includes(type)) {
newItem.h = 32; newItem.h = 32;

View File

@ -23,7 +23,6 @@ import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app"; import { useAppStoreWithOut } from "@/store/modules/app";
import type { AxiosResponse } from "axios"; import type { AxiosResponse } from "axios";
import query from "@/graphql/fetch"; import query from "@/graphql/fetch";
import { useQueryTopologyMetrics } from "@/hooks/useMetricsProcessor";
import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor"; import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
@ -226,9 +225,6 @@ export const topologyStore = defineStore({
setNodeMetricValue(m: MetricVal) { setNodeMetricValue(m: MetricVal) {
this.nodeMetricValue = m; this.nodeMetricValue = m;
}, },
setNodeValue(m: MetricVal) {
this.nodeMetricValue = m;
},
setLegendValues(expressions: string, data: { [key: string]: any }) { setLegendValues(expressions: string, data: { [key: string]: any }) {
for (let idx = 0; idx < this.nodes.length; idx++) { for (let idx = 0; idx < this.nodes.length; idx++) {
for (let index = 0; index < expressions.length; index++) { for (let index = 0; index < expressions.length; index++) {
@ -446,15 +442,6 @@ export const topologyStore = defineStore({
return { calls, nodes }; return { calls, nodes };
}, },
async getNodeMetricValue(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
const res: AxiosResponse = await query(param);
if (res.data.errors) {
return res.data;
}
this.setNodeMetricValue(res.data.data);
return res.data;
},
async getNodeExpressionValue(param: { queryStr: string; conditions: { [key: string]: unknown } }) { async getNodeExpressionValue(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
const res: AxiosResponse = await query(param); const res: AxiosResponse = await query(param);
@ -464,38 +451,6 @@ export const topologyStore = defineStore({
return res.data; return res.data;
}, },
async getLinkClientMetrics(linkClientMetrics: string[]) {
if (!linkClientMetrics.length) {
this.setLinkClientMetrics({});
return;
}
const idsC = this.calls.filter((i: Call) => i.detectPoints.includes("CLIENT")).map((b: Call) => b.id);
if (!idsC.length) {
return;
}
const param = await useQueryTopologyMetrics(linkClientMetrics, idsC);
const res = await this.getCallClientMetrics(param);
if (res.errors) {
ElMessage.error(res.errors);
}
},
async getLinkServerMetrics(linkServerMetrics: string[]) {
if (!linkServerMetrics.length) {
this.setLinkServerMetrics({});
return;
}
const idsS = this.calls.filter((i: Call) => i.detectPoints.includes("SERVER")).map((b: Call) => b.id);
if (!idsS.length) {
return;
}
const param = await useQueryTopologyMetrics(linkServerMetrics, idsS);
const res = await this.getCallServerMetrics(param);
if (res.errors) {
ElMessage.error(res.errors);
}
},
async getLinkExpressions(expressions: string[], type: string) { async getLinkExpressions(expressions: string[], type: string) {
if (!expressions.length) { if (!expressions.length) {
this.setLinkServerMetrics({}); this.setLinkServerMetrics({});
@ -519,22 +474,6 @@ export const topologyStore = defineStore({
this.setLinkClientMetrics(metrics); this.setLinkClientMetrics(metrics);
} }
}, },
async queryNodeMetrics(nodeMetrics: string[]) {
if (!nodeMetrics.length) {
this.setNodeMetricValue({});
return;
}
const ids = this.nodes.map((d: Node) => d.id);
if (!ids.length) {
return;
}
const param = await useQueryTopologyMetrics(nodeMetrics, ids);
const res = await this.getNodeMetricValue(param);
if (res.errors) {
ElMessage.error(res.errors);
}
},
async queryNodeExpressions(expressions: string[]) { async queryNodeExpressions(expressions: string[]) {
if (!expressions.length) { if (!expressions.length) {
this.setNodeMetricValue({}); this.setNodeMetricValue({});
@ -557,44 +496,6 @@ export const topologyStore = defineStore({
const metrics = handleExpressionValues(res.data); const metrics = handleExpressionValues(res.data);
this.setNodeMetricValue(metrics); this.setNodeMetricValue(metrics);
}, },
async getLegendMetrics(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
const res: AxiosResponse = await query(param);
if (res.data.errors) {
return res.data;
}
const data = res.data.data;
const metrics = Object.keys(data);
this.nodes = this.nodes.map((d: Node & Recordable) => {
for (const m of metrics) {
for (const val of data[m].values) {
if (d.id === val.id) {
d[m] = val.value;
}
}
}
return d;
});
return res.data;
},
async getCallServerMetrics(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
const res: AxiosResponse = await query(param);
if (res.data.errors) {
return res.data;
}
this.setLinkServerMetrics(res.data.data);
return res.data;
},
async getCallClientMetrics(param: { queryStr: string; conditions: { [key: string]: unknown } }) {
const res: AxiosResponse = await query(param);
if (res.data.errors) {
return res.data;
}
this.setLinkClientMetrics(res.data.data);
return res.data;
},
async getHierarchyServiceTopology() { async getHierarchyServiceTopology() {
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const { currentService } = useSelectorStore(); const { currentService } = useSelectorStore();

View File

@ -32,12 +32,9 @@ export interface LayoutConfig {
h: number; h: number;
i: string; i: string;
type: string; type: string;
metricMode?: string;
widget?: WidgetConfig; widget?: WidgetConfig;
graph?: GraphConfig; graph?: GraphConfig;
metrics?: string[];
expressions?: string[]; expressions?: string[];
metricTypes?: string[];
typesOfMQE?: string[]; typesOfMQE?: string[];
children?: { name: string; children: LayoutConfig[]; expression?: string; enable?: boolean }[]; children?: { name: string; children: LayoutConfig[]; expression?: string; enable?: boolean }[];
activedTabIndex?: number; activedTabIndex?: number;
@ -77,7 +74,6 @@ export type Filters = {
export type MetricConfigOpt = { export type MetricConfigOpt = {
unit?: string; unit?: string;
label?: string; label?: string;
calculation?: string;
labelsIndex?: string; labelsIndex?: string;
sortOrder?: string; sortOrder?: string;
topN?: number; topN?: number;

View File

@ -304,6 +304,7 @@ limitations under the License. -->
async function importTemplates(event: any) { async function importTemplates(event: any) {
const arr: any = await readFile(event); const arr: any = await readFile(event);
for (const item of arr) { for (const item of arr) {
const { layer, name, entity } = item.configuration; const { layer, name, entity } = item.configuration;
const index = dashboardStore.dashboards.findIndex( const index = dashboardStore.dashboards.findIndex(
@ -359,6 +360,20 @@ limitations under the License. -->
multipleTableRef.value!.clearSelection(); multipleTableRef.value!.clearSelection();
}, 2000); }, 2000);
} }
function removeUnusedConfig(config: any) {
// remove `General` metrics config
delete config.metrics;
delete config.metricTypes;
delete config.metricMode;
delete config.linkServerMetrics;
delete config.linkClientMetrics;
delete config.nodeMetric;
if (([WidgetType.Topology] as string[]).includes(config.type)) {
delete config.legend;
}
}
function optimizeTemplate( function optimizeTemplate(
children: (LayoutConfig & { children: (LayoutConfig & {
moved?: boolean; moved?: boolean;
@ -390,17 +405,8 @@ limitations under the License. -->
if (isEmptyObject(child.widget)) { if (isEmptyObject(child.widget)) {
delete child.widget; delete child.widget;
} }
if (!(child.metrics && child.metrics.length && child.metrics[0])) {
delete child.metrics;
}
if (!(child.metricTypes && child.metricTypes.length && child.metricTypes[0])) {
delete child.metricTypes;
}
if (child.metricConfig && child.metricConfig.length) { if (child.metricConfig && child.metricConfig.length) {
child.metricConfig.forEach((c, index) => { child.metricConfig.forEach((c, index) => {
if (!c.calculation) {
delete c.calculation;
}
if (!c.unit) { if (!c.unit) {
delete c.unit; delete c.unit;
} }
@ -415,6 +421,7 @@ limitations under the License. -->
if (!(child.metricConfig && child.metricConfig.length)) { if (!(child.metricConfig && child.metricConfig.length)) {
delete child.metricConfig; delete child.metricConfig;
} }
removeUnusedConfig(child);
if (child.type === WidgetType.Tab) { if (child.type === WidgetType.Tab) {
for (const item of child.children || []) { for (const item of child.children || []) {
optimizeTemplate(item.children); optimizeTemplate(item.children);

View File

@ -32,10 +32,7 @@ limitations under the License. -->
:config="{ :config="{
i: 0, i: 0,
...graph, ...graph,
metrics: config.metrics,
metricTypes: config.metricTypes,
metricConfig: config.metricConfig, metricConfig: config.metricConfig,
metricMode: config.metricMode,
expressions: config.expressions || [], expressions: config.expressions || [],
typesOfMQE: typesOfMQE || [], typesOfMQE: typesOfMQE || [],
subExpressions: config.subExpressions || [], subExpressions: config.subExpressions || [],
@ -56,12 +53,10 @@ limitations under the License. -->
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { useSelectorStore } from "@/store/modules/selectors"; import { useSelectorStore } from "@/store/modules/selectors";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { useQueryProcessor, useSourceProcessor } from "@/hooks/useMetricsProcessor";
import { useExpressionsQueryProcessor } from "@/hooks/useExpressionsProcessor"; import { useExpressionsQueryProcessor } from "@/hooks/useExpressionsProcessor";
import graphs from "./graphs"; import graphs from "./graphs";
import { EntityType } from "./data"; import { EntityType } from "./data";
import timeFormat from "@/utils/timeFormat"; import timeFormat from "@/utils/timeFormat";
import { MetricModes } from "./data";
export default defineComponent({ export default defineComponent({
name: "WidgetPage", name: "WidgetPage",
@ -132,37 +127,16 @@ limitations under the License. -->
} }
} }
async function queryMetrics() { async function queryMetrics() {
const isExpression = config.value.metricMode === MetricModes.Expression;
if (isExpression) {
loading.value = true;
const params = await useExpressionsQueryProcessor({
metrics: config.value.expressions || [],
metricConfig: config.value.metricConfig || [],
subExpressions: config.value.subExpressions || [],
});
loading.value = false;
source.value = params.source || {};
typesOfMQE.value = params.typesOfMQE;
return;
}
const params = await useQueryProcessor({ ...config.value });
if (!params) {
source.value = {};
return;
}
loading.value = true; loading.value = true;
const json = await dashboardStore.fetchMetricValue(params); const params = await useExpressionsQueryProcessor({
loading.value = false; metrics: config.value.expressions || [],
if (!json) {
return;
}
const d = {
metrics: config.value.metrics || [],
metricTypes: config.value.metricTypes || [],
metricConfig: config.value.metricConfig || [], metricConfig: config.value.metricConfig || [],
}; subExpressions: config.value.subExpressions || [],
source.value = await useSourceProcessor(json, d); });
loading.value = false;
source.value = params.source || {};
typesOfMQE.value = params.typesOfMQE;
} }
watch( watch(
() => appStoreWithOut.durationTime, () => appStoreWithOut.durationTime,
@ -209,7 +183,7 @@ limitations under the License. -->
height: 25px; height: 25px;
line-height: 25px; line-height: 25px;
text-align: center; text-align: center;
background-color: aliceblue; background-color: var(--sw-config-header);
font-size: $font-size-smaller; font-size: $font-size-smaller;
position: relative; position: relative;
} }

View File

@ -54,7 +54,6 @@ limitations under the License. -->
import copy from "@/utils/copy"; import copy from "@/utils/copy";
import { RefreshOptions } from "@/views/dashboard/data"; import { RefreshOptions } from "@/views/dashboard/data";
import { TimeType } from "@/constants/data"; import { TimeType } from "@/constants/data";
import { MetricModes } from "../data";
const { t } = useI18n(); const { t } = useI18n();
const appStore = useAppStoreWithOut(); const appStore = useAppStoreWithOut();
@ -88,8 +87,7 @@ limitations under the License. -->
step: appStore.durationRow.step, step: appStore.durationRow.step,
utc: appStore.utc, utc: appStore.utc,
}); });
const { widget, graph, metrics, metricTypes, metricConfig, metricMode, expressions, typesOfMQE, subExpressions } = const { widget, graph, metricConfig, expressions, typesOfMQE, subExpressions } = dashboardStore.selectedGrid;
dashboardStore.selectedGrid;
const c = (metricConfig || []).map((d: any) => { const c = (metricConfig || []).map((d: any) => {
const t: any = {}; const t: any = {};
if (d.label) { if (d.label) {
@ -103,19 +101,13 @@ limitations under the License. -->
const opt: any = { const opt: any = {
type: dashboardStore.selectedGrid.type, type: dashboardStore.selectedGrid.type,
graph: graph, graph: graph,
metricMode,
metricConfig: c, metricConfig: c,
height: dashboardStore.selectedGrid.h * 20 + 60, height: dashboardStore.selectedGrid.h * 20 + 60,
}; };
if (metricMode === MetricModes.Expression) { opt.expressions = expressions;
opt.expressions = expressions; opt.typesOfMQE = typesOfMQE;
opt.typesOfMQE = typesOfMQE; if (subExpressions && subExpressions.length) {
if (subExpressions && subExpressions.length) { opt.subExpressions = subExpressions;
opt.subExpressions = subExpressions;
}
} else {
opt.metrics = metrics;
opt.metricTypes = metricTypes;
} }
if (widget) { if (widget) {
opt.widget = { opt.widget = {

View File

@ -34,11 +34,8 @@ limitations under the License. -->
...graph, ...graph,
legend: (dashboardStore.selectedGrid.graph || {}).legend, legend: (dashboardStore.selectedGrid.graph || {}).legend,
i: dashboardStore.selectedGrid.i, i: dashboardStore.selectedGrid.i,
metrics: dashboardStore.selectedGrid.metrics,
metricTypes: dashboardStore.selectedGrid.metricTypes,
metricConfig: dashboardStore.selectedGrid.metricConfig, metricConfig: dashboardStore.selectedGrid.metricConfig,
relatedTrace: dashboardStore.selectedGrid.relatedTrace, relatedTrace: dashboardStore.selectedGrid.relatedTrace,
metricMode: dashboardStore.selectedGrid.metricMode,
expressions: dashboardStore.selectedGrid.expressions || [], expressions: dashboardStore.selectedGrid.expressions || [],
typesOfMQE: dashboardStore.selectedGrid.typesOfMQE || [], typesOfMQE: dashboardStore.selectedGrid.typesOfMQE || [],
subExpressions: dashboardStore.selectedGrid.subExpressions || [], subExpressions: dashboardStore.selectedGrid.subExpressions || [],
@ -89,7 +86,6 @@ limitations under the License. -->
import type { Option } from "@/types/app"; import type { Option } from "@/types/app";
import graphs from "../graphs"; import graphs from "../graphs";
import CustomOptions from "./widget/index"; import CustomOptions from "./widget/index";
import { MetricModes } from "../data";
export default defineComponent({ export default defineComponent({
name: "WidgetEdit", name: "WidgetEdit",
@ -142,23 +138,6 @@ limitations under the License. -->
function applyConfig() { function applyConfig() {
dashboardStore.setConfigPanel(false); dashboardStore.setConfigPanel(false);
const { metricMode } = dashboardStore.selectedGrid;
let p = {};
if (metricMode === MetricModes.Expression) {
p = {
metrics: [],
metricTypes: [],
};
} else {
p = {
expressions: [],
typesOfMQE: [],
};
}
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
...p,
});
dashboardStore.setConfigs(dashboardStore.selectedGrid); dashboardStore.setConfigs(dashboardStore.selectedGrid);
} }

View File

@ -25,28 +25,20 @@ limitations under the License. -->
:clearable="true" :clearable="true"
/> />
</div> </div>
<div>{{ t("metrics") }}</div>
<div class="flex-h"> <div class="flex-h">
<el-switch <div>{{ t("metrics") }}</div>
v-model="isExpression" <div class="link">
class="mb-5"
active-text="Expressions"
inactive-text="General"
size="small"
@change="changeMetricMode"
/>
<div class="ml-5 link">
<a target="_blank" href="https://skywalking.apache.org/docs/main/next/en/api/metrics-query-expression/"> <a target="_blank" href="https://skywalking.apache.org/docs/main/next/en/api/metrics-query-expression/">
<Icon iconName="info_outline" size="middle" /> <Icon iconName="info_outline" size="middle" />
</a> </a>
</div> </div>
</div> </div>
<div v-if="isExpression && states.isList"> <div v-if="states.isList">
<span class="title">{{ t("summary") }}</span> <span class="title">{{ t("summary") }}</span>
<span>{{ t("detail") }}</span> <span>{{ t("detail") }}</span>
</div> </div>
<div v-for="(metric, index) in states.metrics" :key="index" class="mb-10"> <div v-for="(metric, index) in states.metrics" :key="index" class="mb-10">
<span v-if="isExpression"> <span>
<div class="expression-param" contenteditable="true" @blur="changeExpression($event, index)"> <div class="expression-param" contenteditable="true" @blur="changeExpression($event, index)">
{{ metric }} {{ metric }}
</div> </div>
@ -59,24 +51,6 @@ limitations under the License. -->
{{ states.subMetrics[index] }} {{ states.subMetrics[index] }}
</div> </div>
</span> </span>
<span v-else>
<Selector
:value="metric"
:options="states.metricList"
size="small"
placeholder="Select a metric"
@change="changeMetrics(index, $event)"
class="selectors"
/>
<Selector
:value="states.metricTypes[index]"
:options="states.metricTypeList[index]"
size="small"
:disabled="graph.type && !states.isList && index !== 0"
@change="changeMetricType(index, $event)"
class="selectors"
/>
</span>
<el-popover placement="top" :width="400" trigger="click"> <el-popover placement="top" :width="400" trigger="click">
<template #reference> <template #reference>
<span @click="setMetricConfig(index)"> <span @click="setMetricConfig(index)">
@ -88,7 +62,7 @@ limitations under the License. -->
<span <span
v-show=" v-show="
states.isList || states.isList ||
[ProtocolTypes.ReadMetricsValues, ExpressionResultType.TIME_SERIES_VALUES as string].includes(states.metricTypes[0]) [ExpressionResultType.TIME_SERIES_VALUES as string].includes(states.metricTypes[0])
" "
> >
<Icon <Icon
@ -100,13 +74,13 @@ limitations under the License. -->
/> />
<Icon class="cp" iconName="remove_circle_outline" size="middle" @click="deleteMetric(index)" /> <Icon class="cp" iconName="remove_circle_outline" size="middle" @click="deleteMetric(index)" />
</span> </span>
<div v-if="(states.tips || [])[index] && isExpression" class="ml-10 red sm"> <div v-if="(states.tips || [])[index]" class="ml-10 red sm">
{{ states.tips[index] }} {{ states.tips[index] }}
</div> </div>
<div v-if="(errors || [])[index] && isExpression" class="ml-10 red sm"> <div v-if="(errors || [])[index]" class="ml-10 red sm">
{{ (errors || [])[index] }} {{ (errors || [])[index] }}
</div> </div>
<div v-if="(subErrors || [])[index] && isExpression" class="ml-10 red sm"> <div v-if="(subErrors || [])[index]" class="ml-10 red sm">
{{ (subErrors || [])[index] }} {{ (subErrors || [])[index] }}
</div> </div>
</div> </div>
@ -128,20 +102,14 @@ limitations under the License. -->
import type { Option } from "@/types/app"; import type { Option } from "@/types/app";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { import {
MetricTypes,
ListChartTypes, ListChartTypes,
DefaultGraphConfig, DefaultGraphConfig,
EntityType, EntityType,
ChartTypes, ChartTypes,
PodsChartTypes, PodsChartTypes,
MetricsType,
ProtocolTypes,
ExpressionResultType, ExpressionResultType,
MetricModes, } from "@/views/dashboard/data";
} from "../../../data";
import { ElMessage } from "element-plus";
import Icon from "@/components/Icon.vue"; import Icon from "@/components/Icon.vue";
import { useQueryProcessor, useSourceProcessor } from "@/hooks/useMetricsProcessor";
import { useExpressionsQueryProcessor } from "@/hooks/useExpressionsProcessor"; import { useExpressionsQueryProcessor } from "@/hooks/useExpressionsProcessor";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import type { DashboardItem, MetricConfigOpt } from "@/types/dashboard"; import type { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
@ -160,16 +128,11 @@ limitations under the License. -->
}, },
}); });
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const isExpression = ref<boolean>(dashboardStore.selectedGrid.metricMode === MetricModes.Expression); const metrics = computed(() => dashboardStore.selectedGrid.expressions || []);
const metrics = computed( const subMetrics = computed(() => dashboardStore.selectedGrid.subExpressions || []);
() => (isExpression.value ? dashboardStore.selectedGrid.expressions : dashboardStore.selectedGrid.metrics) || [], const subMetricTypes = computed(() => dashboardStore.selectedGrid.subTypesOfMQE || []);
);
const subMetrics = computed(() => (isExpression.value ? dashboardStore.selectedGrid.subExpressions : []) || []);
const subMetricTypes = computed(() => (isExpression.value ? dashboardStore.selectedGrid.subTypesOfMQE : []) || []);
const graph = computed(() => dashboardStore.selectedGrid.graph || {}); const graph = computed(() => dashboardStore.selectedGrid.graph || {});
const metricTypes = computed( const typesOfMQE = computed(() => dashboardStore.selectedGrid.typesOfMQE || []);
() => (isExpression.value ? dashboardStore.selectedGrid.typesOfMQE : dashboardStore.selectedGrid.metricTypes) || [],
);
const states = reactive<{ const states = reactive<{
metrics: string[]; metrics: string[];
subMetrics: string[]; subMetrics: string[];
@ -177,17 +140,15 @@ limitations under the License. -->
metricTypes: string[]; metricTypes: string[];
metricTypeList: Option[][]; metricTypeList: Option[][];
isList: boolean; isList: boolean;
metricList: (Option & { type: string })[];
dashboardName: string; dashboardName: string;
dashboardList: ((DashboardItem & { label: string; value: string }) | any)[]; dashboardList: ((DashboardItem & { label: string; value: string }) | any)[];
tips: string[]; tips: string[];
subTips: string[]; subTips: string[];
}>({ }>({
metrics: metrics.value.length ? metrics.value : [""], metrics: metrics.value.length ? metrics.value : [""],
metricTypes: metricTypes.value.length ? metricTypes.value : [""], metricTypes: typesOfMQE.value.length ? typesOfMQE.value : [""],
metricTypeList: [], metricTypeList: [],
isList: false, isList: false,
metricList: [],
dashboardName: graph.value.dashboardName, dashboardName: graph.value.dashboardName,
dashboardList: [{ label: "", value: "" }], dashboardList: [{ label: "", value: "" }],
tips: [], tips: [],
@ -199,16 +160,13 @@ limitations under the License. -->
unit: "", unit: "",
label: "", label: "",
labelsIndex: "", labelsIndex: "",
calculation: "",
sortOrder: "DES", sortOrder: "DES",
}); });
states.isList = ListChartTypes.includes(graph.value.type); states.isList = ListChartTypes.includes(graph.value.type);
const defaultLen = ref<number>(states.isList ? 5 : 20); const defaultLen = ref<number>(states.isList ? 5 : 20);
const backupMetricConfig = ref<MetricConfigOpt[]>([]);
setDashboards(); setDashboards();
setMetricType();
const setVisTypes = computed(() => { const setVisTypes = computed(() => {
let graphs = []; let graphs = [];
@ -223,70 +181,6 @@ limitations under the License. -->
return graphs; return graphs;
}); });
async function setMetricType(chart?: any) {
const g = chart || dashboardStore.selectedGrid.graph || {};
let arr: any[] = states.metricList;
if (!chart) {
const json = await dashboardStore.fetchMetricList();
if (json.errors) {
ElMessage.error(json.errors);
return;
}
arr = json.data.metrics;
}
states.metricList = (arr || []).filter((d: { type: string }) => {
if (states.isList) {
if (d.type === MetricsType.REGULAR_VALUE || d.type === MetricsType.LABELED_VALUE) {
return d;
}
} else if (g.type === "Table") {
if (d.type === MetricsType.LABELED_VALUE || d.type === MetricsType.REGULAR_VALUE) {
return d;
}
} else {
return d;
}
});
if (isExpression.value) {
if (states.metrics && states.metrics[0]) {
queryMetrics();
} else {
emit("update", {});
}
return;
}
const metrics: any = states.metricList.filter((d: { value: string; type: string }) =>
states.metrics.includes(d.value),
);
if (metrics.length) {
// keep states.metrics index
const m = metrics.map((d: { value: string }) => d.value);
states.metrics = states.metrics.filter((d) => m.includes(d));
} else {
states.metrics = [""];
states.metricTypes = [""];
}
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
metrics: states.metrics,
metricTypes: states.metricTypes,
graph: g,
});
states.metricTypeList = [];
for (const metric of metrics) {
if (states.metrics.includes(metric.value)) {
const arr = setMetricTypeList(metric.type);
states.metricTypeList.push(arr);
}
}
if (states.metrics && states.metrics[0]) {
queryMetrics();
} else {
emit("update", {});
}
}
function setDashboards(type?: string) { function setDashboards(type?: string) {
const chart = type || (dashboardStore.selectedGrid.graph && dashboardStore.selectedGrid.graph.type); const chart = type || (dashboardStore.selectedGrid.graph && dashboardStore.selectedGrid.graph.type);
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]"); const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
@ -308,6 +202,11 @@ limitations under the License. -->
}, []); }, []);
states.dashboardList = arr.length ? arr : [{ label: "", value: "" }]; states.dashboardList = arr.length ? arr : [{ label: "", value: "" }];
if (states.metrics && states.metrics[0]) {
queryMetrics();
} else {
emit("update", {});
}
} }
function changeChartType(item: Option) { function changeChartType(item: Option) {
@ -316,8 +215,6 @@ limitations under the License. -->
if (states.isList) { if (states.isList) {
dashboardStore.selectWidget({ dashboardStore.selectWidget({
...dashboardStore.selectedGrid, ...dashboardStore.selectedGrid,
metrics: [""],
metricTypes: [""],
expressions: [""], expressions: [""],
typesOfMQE: [""], typesOfMQE: [""],
}); });
@ -326,97 +223,20 @@ limitations under the License. -->
defaultLen.value = 5; defaultLen.value = 5;
} }
if (isExpression.value) { dashboardStore.selectWidget({
dashboardStore.selectWidget({ ...dashboardStore.selectedGrid,
...dashboardStore.selectedGrid, graph: chart,
graph: chart, });
});
} else {
setMetricType(chart);
}
setDashboards(chart.type); setDashboards(chart.type);
states.dashboardName = ""; states.dashboardName = "";
defaultLen.value = 10; defaultLen.value = 10;
} }
function changeMetrics(index: number, arr: (Option & { type: string })[] | any) {
if (!arr.length) {
states.metricTypeList = [];
states.metricTypes = [];
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
...{ metricTypes: states.metricTypes, metrics: states.metrics },
});
return;
}
states.metrics[index] = arr[0].value;
const typeOfMetrics = arr[0].type;
states.metricTypeList[index] = setMetricTypeList(typeOfMetrics);
states.metricTypes[index] = MetricTypes[typeOfMetrics][0].value;
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
...{ metricTypes: states.metricTypes, metrics: states.metrics },
});
if (states.isList) {
return;
}
queryMetrics();
}
function changeMetricType(index: number, opt: Option[] | any) {
const metric = states.metricList.filter((d: Option) => states.metrics[index] === d.value)[0] || {};
const l = setMetricTypeList(metric.type);
if (states.isList) {
states.metricTypes[index] = opt[0].value;
states.metricTypeList[index] = l;
} else {
states.metricTypes = states.metricTypes.map((d: string) => {
d = opt[0].value;
return d;
});
states.metricTypeList = states.metricTypeList.map((d: Option[]) => {
d = l;
return d;
});
}
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
...{ metricTypes: states.metricTypes },
});
if (states.isList) {
return;
}
queryMetrics();
}
async function queryMetrics() { async function queryMetrics() {
if (states.isList) { if (states.isList) {
return; return;
} }
if (isExpression.value) { queryMetricsWithExpressions();
queryMetricsWithExpressions();
return;
}
const { metricConfig, metricTypes, metrics } = dashboardStore.selectedGrid;
if (!(metrics && metrics[0] && metricTypes && metricTypes[0])) {
return emit("update", {});
}
const params = useQueryProcessor({ ...states, metricConfig });
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, { ...states, metricConfig });
emit("update", source);
} }
async function queryMetricsWithExpressions() { async function queryMetricsWithExpressions() {
@ -432,7 +252,6 @@ limitations under the License. -->
...dashboardStore.selectedGrid, ...dashboardStore.selectedGrid,
typesOfMQE: states.metricTypes, typesOfMQE: states.metricTypes,
}); });
emit("update", params.source || {}); emit("update", params.source || {});
} }
@ -454,43 +273,32 @@ limitations under the License. -->
function addMetric() { function addMetric() {
states.metrics.push(""); states.metrics.push("");
states.tips.push(""); states.tips.push("");
if (isExpression.value && states.isList) { if (states.isList) {
states.subMetrics.push(""); states.subMetrics.push("");
states.subTips.push(""); states.subTips.push("");
} }
if (!states.isList) { if (!states.isList) {
states.metricTypes.push(states.metricTypes[0]); states.metricTypes.push(states.metricTypes[0]);
if (!isExpression.value) {
states.metricTypeList.push(states.metricTypeList[0]);
}
return; return;
} }
states.metricTypes.push(""); states.metricTypes.push("");
if (isExpression.value && states.isList) {
states.subMetricTypes.push("");
}
} }
function deleteMetric(index: number) { function deleteMetric(index: number) {
if (states.metrics.length === 1) { if (states.metrics.length === 1) {
states.metrics = [""]; states.metrics = [""];
states.metricTypes = [""]; states.metricTypes = [""];
states.tips = [""]; states.tips = [""];
let v = {}; let v: any = { typesOfMQE: states.metricTypes, expressions: states.metrics };
if (isExpression.value) { if (states.isList) {
v = { typesOfMQE: states.metricTypes, expressions: states.metrics }; states.subMetrics = [""];
if (states.isList) { states.subMetricTypes = [""];
states.subMetrics = [""]; states.subTips = [""];
states.subMetricTypes = [""]; v = {
states.subTips = [""]; ...v,
v = { subTypesOfMQE: states.subMetricTypes,
...v, subExpressions: states.subMetrics,
subTypesOfMQE: states.subMetricTypes, };
subExpressions: states.subMetrics,
};
}
} else {
v = { metricTypes: states.metricTypes, metrics: states.metrics };
} }
dashboardStore.selectWidget({ dashboardStore.selectWidget({
...dashboardStore.selectedGrid, ...dashboardStore.selectedGrid,
@ -505,37 +313,26 @@ limitations under the License. -->
const config = dashboardStore.selectedGrid.metricConfig || []; const config = dashboardStore.selectedGrid.metricConfig || [];
const metricConfig = config[index] ? config.splice(index, 1) : config; const metricConfig = config[index] ? config.splice(index, 1) : config;
let p = {}; let p = {};
if (isExpression.value) { if (states.isList) {
if (states.isList) { states.subMetrics.splice(index, 1);
states.subMetrics.splice(index, 1); states.subMetricTypes.splice(index, 1);
states.subMetricTypes.splice(index, 1); states.subTips.splice(index, 1);
states.subTips.splice(index, 1); p = {
p = { ...p,
...p, typesOfMQE: states.metricTypes,
typesOfMQE: states.metricTypes, expressions: states.metrics,
expressions: states.metrics, subTypesOfMQE: states.subMetricTypes,
subTypesOfMQE: states.subMetricTypes, subExpressions: states.subMetrics,
subExpressions: states.subMetrics, };
};
}
} else {
p = { metricTypes: states.metricTypes, metrics: states.metrics };
} }
dashboardStore.selectWidget({ dashboardStore.selectWidget({
...dashboardStore.selectedGrid, ...dashboardStore.selectedGrid,
...p, ...p,
metricConfig, metricConfig,
}); });
queryMetrics();
} }
function setMetricTypeList(type: string) {
if (type !== MetricsType.REGULAR_VALUE) {
return MetricTypes[type];
}
if (states.isList || graph.value.type === "Table") {
return [MetricTypes.REGULAR_VALUE[0], MetricTypes.REGULAR_VALUE[1]];
}
return MetricTypes[type];
}
function setMetricConfig(index: number) { function setMetricConfig(index: number) {
const n = { const n = {
unit: "", unit: "",
@ -553,20 +350,6 @@ limitations under the License. -->
...dashboardStore.selectedGrid.metricConfig[index], ...dashboardStore.selectedGrid.metricConfig[index],
}; };
} }
function changeMetricMode() {
states.metrics = metrics.value.length ? metrics.value : [""];
states.subMetrics = subMetrics.value.length ? subMetrics.value : [""];
states.metricTypes = metricTypes.value.length ? metricTypes.value : [""];
states.subMetricTypes = subMetricTypes.value.length ? subMetricTypes.value : [""];
const config = dashboardStore.selectedGrid.metricConfig;
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
metricMode: isExpression.value ? MetricModes.Expression : MetricModes.General,
metricConfig: backupMetricConfig.value,
});
backupMetricConfig.value = config;
queryMetrics();
}
async function changeExpression(event: any, index: number) { async function changeExpression(event: any, index: number) {
const params = (event.target.textContent || "").replace(/\s+/g, ""); const params = (event.target.textContent || "").replace(/\s+/g, "");
@ -651,5 +434,6 @@ limitations under the License. -->
.link { .link {
cursor: pointer; cursor: pointer;
color: $active-color; color: $active-color;
padding-left: 2px;
} }
</style> </style>

View File

@ -28,7 +28,7 @@ limitations under the License. -->
" "
/> />
</div> </div>
<div class="item mb-10" v-if="hasLabel || isExpression"> <div class="item mb-10">
<span class="label">{{ t("labels") }}</span> <span class="label">{{ t("labels") }}</span>
<el-input <el-input
class="input" class="input"
@ -42,7 +42,7 @@ limitations under the License. -->
" "
/> />
</div> </div>
<div class="item mb-10" v-if="isList && isExpression"> <div class="item mb-10" v-if="isList">
<span class="label">{{ t("detailLabel") }}</span> <span class="label">{{ t("detailLabel") }}</span>
<el-input <el-input
class="input" class="input"
@ -56,30 +56,6 @@ limitations under the License. -->
" "
/> />
</div> </div>
<div class="item mb-10" v-if="[ProtocolTypes.ReadLabeledMetricsValues].includes(metricType) && !isExpression">
<span class="label">{{ t("labelsIndex") }}</span>
<el-input
class="input"
v-model="currentMetric.labelsIndex"
size="small"
placeholder="auto"
@change="
updateConfig(index, {
labelsIndex: encodeURIComponent(currentMetric.labelsIndex || ''),
})
"
/>
</div>
<div class="item mb-10" v-show="!isExpression">
<span class="label">{{ t("aggregation") }}</span>
<SelectSingle
:value="currentMetric.calculation"
:options="CalculationOpts"
@change="changeConfigs(index, { calculation: $event })"
class="selectors"
:clearable="true"
/>
</div>
<div class="item mb-10" v-show="isTopn"> <div class="item mb-10" v-show="isTopn">
<span class="label">{{ t("sortOrder") }}</span> <span class="label">{{ t("sortOrder") }}</span>
<SelectSingle <SelectSingle
@ -108,10 +84,9 @@ limitations under the License. -->
import { ref, watch, computed } from "vue"; import { ref, watch, computed } from "vue";
import type { PropType } from "vue"; import type { PropType } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { SortOrder, CalculationOpts, MetricModes } from "../../../data"; import { SortOrder, ExpressionResultType, ListChartTypes } from "@/views/dashboard/data";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
import { ListChartTypes, ProtocolTypes } from "../../../data";
/*global defineEmits, defineProps */ /*global defineEmits, defineProps */
const props = defineProps({ const props = defineProps({
@ -124,30 +99,17 @@ limitations under the License. -->
const { t } = useI18n(); const { t } = useI18n();
const emit = defineEmits(["update"]); const emit = defineEmits(["update"]);
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const isExpression = ref<boolean>(dashboardStore.selectedGrid.metricMode === MetricModes.Expression);
const currentMetric = ref<MetricConfigOpt>({ const currentMetric = ref<MetricConfigOpt>({
...props.currentMetricConfig, ...props.currentMetricConfig,
topN: props.currentMetricConfig.topN || 10, topN: props.currentMetricConfig.topN || 10,
}); });
const metricTypes = computed( const metricTypes = computed(() => dashboardStore.selectedGrid.typesOfMQE || []);
() => (isExpression.value ? dashboardStore.selectedGrid.typesOfMQE : dashboardStore.selectedGrid.metricTypes) || [],
);
const metricType = computed(() => metricTypes.value[props.index]);
const hasLabel = computed(() => {
const graph = dashboardStore.selectedGrid.graph || {};
return (
ListChartTypes.includes(graph.type) ||
[ProtocolTypes.ReadLabeledMetricsValues, ProtocolTypes.ReadMetricsValues].includes(metricType.value)
);
});
const isList = computed(() => { const isList = computed(() => {
const graph = dashboardStore.selectedGrid.graph || {}; const graph = dashboardStore.selectedGrid.graph || {};
return ListChartTypes.includes(graph.type); return ListChartTypes.includes(graph.type);
}); });
const isTopn = computed(() => const isTopn = computed(() =>
[ProtocolTypes.SortMetrics, ProtocolTypes.ReadSampledRecords, ProtocolTypes.ReadRecords].includes( [ExpressionResultType.RECORD_LIST, ExpressionResultType.SORTED_LIST].includes(metricTypes.value[props.index]),
metricTypes.value[props.index],
),
); );
function updateConfig(index: number, param: { [key: string]: string }) { function updateConfig(index: number, param: { [key: string]: string }) {
@ -170,7 +132,6 @@ limitations under the License. -->
watch( watch(
() => props.currentMetricConfig, () => props.currentMetricConfig,
() => { () => {
isExpression.value = dashboardStore.selectedGrid.metricMode === MetricModes.Expression;
currentMetric.value = { currentMetric.value = {
...props.currentMetricConfig, ...props.currentMetricConfig,
topN: Number(props.currentMetricConfig.topN) || 10, topN: Number(props.currentMetricConfig.topN) || 10,

View File

@ -51,15 +51,12 @@ limitations under the License. -->
:data="state.source" :data="state.source"
:config="{ :config="{
...data.graph, ...data.graph,
metrics: data.metrics || [''],
metricTypes: data.metricTypes || [''],
i: data.i, i: data.i,
id: data.id, id: data.id,
metricConfig: data.metricConfig || [], metricConfig: data.metricConfig || [],
filters: data.filters || {}, filters: data.filters || {},
relatedTrace: data.relatedTrace || {}, relatedTrace: data.relatedTrace || {},
associate: data.associate || [], associate: data.associate || [],
metricMode: data.metricMode,
expressions: data.expressions || [], expressions: data.expressions || [],
typesOfMQE: typesOfMQE || [], typesOfMQE: typesOfMQE || [],
subExpressions: data.subExpressions || [], subExpressions: data.subExpressions || [],
@ -81,12 +78,10 @@ limitations under the License. -->
import { useSelectorStore } from "@/store/modules/selectors"; import { useSelectorStore } from "@/store/modules/selectors";
import graphs from "../graphs"; import graphs from "../graphs";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useQueryProcessor, useSourceProcessor } from "@/hooks/useMetricsProcessor";
import { useExpressionsQueryProcessor } from "@/hooks/useExpressionsProcessor"; import { useExpressionsQueryProcessor } from "@/hooks/useExpressionsProcessor";
import { EntityType, ListChartTypes } from "../data"; import { EntityType, ListChartTypes } from "../data";
import type { EventParams } from "@/types/dashboard"; import type { EventParams } from "@/types/dashboard";
import getDashboard from "@/hooks/useDashboardsSession"; import getDashboard from "@/hooks/useDashboardsSession";
import { MetricModes } from "../data";
const props = { const props = {
data: { data: {
@ -121,38 +116,15 @@ limitations under the License. -->
} }
async function queryMetrics() { async function queryMetrics() {
const isExpression = props.data.metricMode === MetricModes.Expression;
if (isExpression) {
loading.value = true;
const e = {
metrics: props.data.expressions || [],
metricConfig: props.data.metricConfig || [],
};
const params = (await useExpressionsQueryProcessor(e)) || {};
loading.value = false;
state.source = params.source || {};
typesOfMQE.value = params.typesOfMQE;
return;
}
const params = await useQueryProcessor({ ...props.data });
if (!params) {
state.source = {};
return;
}
loading.value = true; loading.value = true;
const json = await dashboardStore.fetchMetricValue(params); const e = {
loading.value = false; metrics: props.data.expressions || [],
if (!json) {
return;
}
const d = {
metrics: props.data.metrics || [],
metricTypes: props.data.metricTypes || [],
metricConfig: props.data.metricConfig || [], metricConfig: props.data.metricConfig || [],
}; };
state.source = await useSourceProcessor(json, d); const params = (await useExpressionsQueryProcessor(e)) || {};
loading.value = false;
state.source = params.source || {};
typesOfMQE.value = params.typesOfMQE;
} }
function removeWidget() { function removeWidget() {
@ -188,7 +160,7 @@ limitations under the License. -->
dashboardStore.selectWidget(props.data); dashboardStore.selectWidget(props.data);
} }
watch( watch(
() => [props.data.metricTypes, props.data.metrics, props.data.expressions], () => props.data.expressions,
() => { () => {
if (!dashboardStore.selectedGrid) { if (!dashboardStore.selectedGrid) {
return; return;

View File

@ -34,29 +34,6 @@ export const ChartTypes = [
{ label: "Endpoint List", value: "EndpointList" }, { label: "Endpoint List", value: "EndpointList" },
{ label: "Instance List", value: "InstanceList" }, { label: "Instance List", value: "InstanceList" },
]; ];
export const MetricChartType: any = {
readMetricsValue: [{ label: "Card", value: "Card" }],
readMetricsValues: [
{ label: "Bar", value: "Bar" },
{ label: "Line", value: "Line" },
{ label: "Area", value: "Area" },
],
sortMetrics: [{ label: "Top List", value: "TopList" }],
readLabeledMetricsValues: [{ label: "Line", value: "Line" }],
readHeatMap: [{ label: "Heat Map", value: "HeatMap" }],
readSampledRecords: [{ label: "Top List", value: "TopList" }],
readRecords: [{ label: "Top List", value: "TopList" }],
};
export enum ProtocolTypes {
ReadRecords = "readRecords",
ReadSampledRecords = "readSampledRecords",
SortMetrics = "sortMetrics",
ReadLabeledMetricsValues = "readLabeledMetricsValues",
ReadHeatMap = "readHeatMap",
ReadMetricsValues = "readMetricsValues",
ReadMetricsValue = "readMetricsValue",
}
export enum ExpressionResultType { export enum ExpressionResultType {
UNKNOWN = "UNKNOWN", UNKNOWN = "UNKNOWN",
SINGLE_VALUE = "SINGLE_VALUE", SINGLE_VALUE = "SINGLE_VALUE",
@ -124,34 +101,6 @@ export const DefaultGraphConfig: { [key: string]: any } = {
}, },
}; };
export enum MetricsType {
UNKNOWN = "UNKNOWN",
REGULAR_VALUE = "REGULAR_VALUE",
LABELED_VALUE = "LABELED_VALUE",
HEATMAP = "HEATMAP",
SAMPLED_RECORD = "SAMPLED_RECORD",
}
export const MetricTypes: {
[key: string]: Array<{ label: string; value: string }>;
} = {
REGULAR_VALUE: [
{ label: "read all values in the duration", value: "readMetricsValues" },
{
label: "read the single value in the duration",
value: "readMetricsValue",
},
{ label: "get sorted top N values", value: "sortMetrics" },
],
LABELED_VALUE: [
{
label: "read all values of labels in the duration",
value: "readLabeledMetricsValues",
},
],
HEATMAP: [{ label: "read heatmap values in the duration", value: "readHeatMap" }],
SAMPLED_RECORD: [{ label: "get sorted topN values", value: "readRecords" }],
};
export enum MetricCatalog { export enum MetricCatalog {
SERVICE = "Service", SERVICE = "Service",
SERVICE_INSTANCE = "ServiceInstance", SERVICE_INSTANCE = "ServiceInstance",
@ -292,19 +241,6 @@ export const ScopeType = [
{ value: "Endpoint", label: "Endpoint", key: 3 }, { value: "Endpoint", label: "Endpoint", key: 3 },
{ value: "ServiceInstance", label: "Service Instance", key: 3 }, { value: "ServiceInstance", label: "Service Instance", key: 3 },
]; ];
export const LegendConditions = [
{ label: "&&", value: "and" },
{ label: "||", value: "or" },
];
export const MetricConditions = [
{ label: ">", value: ">" },
{ label: "<", value: "<" },
];
export enum LegendOpt {
NAME = "name",
VALUE = "value",
CONDITION = "condition",
}
export const DepthList = [1, 2, 3, 4, 5].map((item: number) => ({ export const DepthList = [1, 2, 3, 4, 5].map((item: number) => ({
value: item, value: item,
label: String(item), label: String(item),
@ -329,24 +265,6 @@ export const TextColors: { [key: string]: string } = {
purple: "#bf99f8", purple: "#bf99f8",
}; };
export const CalculationOpts = [
{ label: "Percentage", value: "percentage" },
{ label: "Apdex", value: "apdex" },
{ label: "Avg-preview", value: "average" },
{ label: "Percentage + Avg-preview", value: "percentageAvg" },
{ label: "Apdex + Avg-preview", value: "apdexAvg" },
{ label: "Byte to KB", value: "byteToKB" },
{ label: "Byte to MB", value: "byteToMB" },
{ label: "Byte to GB", value: "byteToGB" },
{
label: "Milliseconds to YYYY-MM-DD HH:mm:ss",
value: "convertMilliseconds",
},
{ label: "Seconds to YYYY-MM-DD HH:mm:ss", value: "convertSeconds" },
{ label: "Milliseconds to seconds", value: "msTos" },
{ label: "Seconds to days", value: "secondToDay" },
{ label: "Nanoseconds to milliseconds", value: "nanosecondToMillisecond" },
];
export const RefIdTypes = [ export const RefIdTypes = [
{ label: "Trace ID", value: "traceId" }, { label: "Trace ID", value: "traceId" },
{ label: "None", value: "none" }, { label: "None", value: "none" },
@ -357,10 +275,6 @@ export const RefreshOptions = [
{ label: "Last 7 days", value: "7", step: "DAY" }, { label: "Last 7 days", value: "7", step: "DAY" },
]; ];
export enum MetricModes {
Expression = "Expression",
General = "General",
}
export enum CallTypes { export enum CallTypes {
Server = "SERVER", Server = "SERVER",
Client = "CLIENT", Client = "CLIENT",

View File

@ -40,8 +40,7 @@ limitations under the License. -->
:config="{ :config="{
...config, ...config,
metricConfig, metricConfig,
metricTypes, typesOfMQE,
metricMode,
}" }"
v-if="colMetrics.length" v-if="colMetrics.length"
/> />
@ -58,9 +57,8 @@ limitations under the License. -->
import type { EndpointListConfig } from "@/types/dashboard"; import type { EndpointListConfig } from "@/types/dashboard";
import type { Endpoint } from "@/types/selector"; import type { Endpoint } from "@/types/selector";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useMetricsProcessor";
import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor"; import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor";
import { EntityType, MetricModes } from "../data"; import { EntityType } from "../data";
import router from "@/router"; import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession"; import getDashboard from "@/hooks/useDashboardsSession";
import type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
@ -75,9 +73,6 @@ limitations under the License. -->
type: Object as PropType< type: Object as PropType<
EndpointListConfig & { EndpointListConfig & {
i: string; i: string;
metrics: string[];
metricTypes: string[];
metricMode: string;
expressions: string[]; expressions: string[];
typesOfMQE: string[]; typesOfMQE: string[];
subExpressions: string[]; subExpressions: string[];
@ -85,8 +80,6 @@ limitations under the License. -->
} & { metricConfig: MetricConfigOpt[] } } & { metricConfig: MetricConfigOpt[] }
>, >,
default: () => ({ default: () => ({
metrics: [],
metricTypes: [],
dashboardName: "", dashboardName: "",
fontSize: 12, fontSize: 12,
i: "", i: "",
@ -106,8 +99,7 @@ limitations under the License. -->
const colMetrics = ref<string[]>([]); const colMetrics = ref<string[]>([]);
const colSubMetrics = ref<string[]>([]); const colSubMetrics = ref<string[]>([]);
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []); const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
const metricTypes = ref<string[]>(props.config.metricTypes || []); const typesOfMQE = ref<string[]>(props.config.typesOfMQE || []);
const metricMode = ref<string>(props.config.metricMode);
if (props.needQuery) { if (props.needQuery) {
queryEndpoints(); queryEndpoints();
@ -138,34 +130,7 @@ limitations under the License. -->
merge: d.merge, merge: d.merge,
}; };
}); });
if (props.config.metricMode === MetricModes.Expression) { queryEndpointExpressions(currentPods);
queryEndpointExpressions(currentPods);
return;
}
const metrics = props.config.metrics || [];
const types = props.config.metricTypes || [];
if (metrics.length && metrics[0] && types.length && types[0]) {
const params = await useQueryPodsMetrics(currentPods, props.config, EntityType[2].value);
const json = await dashboardStore.fetchMetricValue(params);
if (json.errors) {
ElMessage.error(json.errors);
return;
}
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;
colMetrics.value = [];
metricTypes.value = [];
metricConfig.value = [];
} }
async function queryEndpointExpressions(currentPods: Endpoint[]) { async function queryEndpointExpressions(currentPods: Endpoint[]) {
const expressions = props.config.expressions || []; const expressions = props.config.expressions || [];
@ -180,8 +145,8 @@ limitations under the License. -->
endpoints.value = params.data; endpoints.value = params.data;
colMetrics.value = params.names; colMetrics.value = params.names;
colSubMetrics.value = params.subNames; colSubMetrics.value = params.subNames;
metricTypes.value = params.metricTypesArr;
metricConfig.value = params.metricConfigArr; metricConfig.value = params.metricConfigArr;
typesOfMQE.value = params.metricTypesArr;
emit("expressionTips", { tips: params.expressionsTips, subTips: params.subExpressionsTips }); emit("expressionTips", { tips: params.expressionsTips, subTips: params.subExpressionsTips });
return; return;
@ -189,8 +154,8 @@ limitations under the License. -->
endpoints.value = currentPods; endpoints.value = currentPods;
colMetrics.value = []; colMetrics.value = [];
colSubMetrics.value = []; colSubMetrics.value = [];
metricTypes.value = [];
metricConfig.value = []; metricConfig.value = [];
typesOfMQE.value = [];
emit("expressionTips", [], []); emit("expressionTips", [], []);
} }
function clickEndpoint(scope: any) { function clickEndpoint(scope: any) {
@ -212,19 +177,15 @@ limitations under the License. -->
} }
watch( watch(
() => [ () => [
...(props.config.metricTypes || []),
...(props.config.metrics || []),
...(props.config.metricConfig || []), ...(props.config.metricConfig || []),
...(props.config.expressions || []), ...(props.config.expressions || []),
...(props.config.subExpressions || []), ...(props.config.subExpressions || []),
props.config.metricMode,
], ],
(data, old) => { (data, old) => {
if (JSON.stringify(data) === JSON.stringify(old)) { if (JSON.stringify(data) === JSON.stringify(old)) {
return; return;
} }
metricConfig.value = props.config.metricConfig; metricConfig.value = props.config.metricConfig;
metricMode.value = props.config.metricMode;
queryEndpointMetrics(endpoints.value); queryEndpointMetrics(endpoints.value);
}, },
); );

View File

@ -39,8 +39,7 @@ limitations under the License. -->
:config="{ :config="{
...config, ...config,
metricConfig, metricConfig,
metricTypes, typesOfMQE,
metricMode,
}" }"
v-if="colMetrics.length" v-if="colMetrics.length"
/> />
@ -85,9 +84,8 @@ limitations under the License. -->
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import type { InstanceListConfig } from "@/types/dashboard"; import type { InstanceListConfig } from "@/types/dashboard";
import type { Instance } from "@/types/selector"; import type { Instance } from "@/types/selector";
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useMetricsProcessor";
import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor"; import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor";
import { EntityType, MetricModes } from "../data"; import { EntityType } from "../data";
import router from "@/router"; import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession"; import getDashboard from "@/hooks/useDashboardsSession";
import type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
@ -100,9 +98,7 @@ limitations under the License. -->
InstanceListConfig & { InstanceListConfig & {
i: string; i: string;
metrics: string[]; metrics: string[];
metricTypes: string[];
isEdit: boolean; isEdit: boolean;
metricMode: string;
expressions: string[]; expressions: string[];
typesOfMQE: string[]; typesOfMQE: string[];
subExpressions: string[]; subExpressions: string[];
@ -114,7 +110,7 @@ limitations under the License. -->
fontSize: 12, fontSize: 12,
i: "", i: "",
metrics: [], metrics: [],
metricTypes: [], typesOfMQE: [],
}), }),
}, },
intervalTime: { type: Array as PropType<string[]>, default: () => [] }, intervalTime: { type: Array as PropType<string[]>, default: () => [] },
@ -131,9 +127,9 @@ limitations under the License. -->
const colMetrics = ref<string[]>([]); const colMetrics = ref<string[]>([]);
const colSubMetrics = ref<string[]>([]); const colSubMetrics = ref<string[]>([]);
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []); const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
const metricTypes = ref<string[]>(props.config.metricTypes || []);
const pods = ref<Instance[]>([]); // all instances const pods = ref<Instance[]>([]); // all instances
const metricMode = ref<string>(props.config.metricMode); const typesOfMQE = ref<string[]>(props.config.typesOfMQE || []);
if (props.needQuery) { if (props.needQuery) {
queryInstance(); queryInstance();
} }
@ -169,36 +165,7 @@ limitations under the License. -->
attributes: d.attributes, attributes: d.attributes,
}; };
}); });
if (props.config.metricMode === MetricModes.Expression) { queryInstanceExpressions(currentInstances);
queryInstanceExpressions(currentInstances);
return;
}
const metrics = props.config.metrics || [];
const types = props.config.metricTypes || [];
if (metrics.length && metrics[0] && types.length && types[0]) {
const params = await useQueryPodsMetrics(currentInstances, props.config, EntityType[3].value);
const json = await dashboardStore.fetchMetricValue(params);
if (json.errors) {
ElMessage.error(json.errors);
return;
}
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;
colMetrics.value = [];
metricTypes.value = [];
metricConfig.value = [];
} }
async function queryInstanceExpressions(currentInstances: Instance[]) { async function queryInstanceExpressions(currentInstances: Instance[]) {
@ -214,7 +181,7 @@ limitations under the License. -->
instances.value = params.data; instances.value = params.data;
colMetrics.value = params.names; colMetrics.value = params.names;
colSubMetrics.value = params.subNames; colSubMetrics.value = params.subNames;
metricTypes.value = params.metricTypesArr; typesOfMQE.value = params.metricTypesArr;
metricConfig.value = params.metricConfigArr; metricConfig.value = params.metricConfigArr;
emit("expressionTips", { tips: params.expressionsTips, subTips: params.subExpressionsTips }); emit("expressionTips", { tips: params.expressionsTips, subTips: params.subExpressionsTips });
@ -223,7 +190,7 @@ limitations under the License. -->
instances.value = currentInstances; instances.value = currentInstances;
colSubMetrics.value = []; colSubMetrics.value = [];
colMetrics.value = []; colMetrics.value = [];
metricTypes.value = []; typesOfMQE.value = [];
metricConfig.value = []; metricConfig.value = [];
emit("expressionTips", [], []); emit("expressionTips", [], []);
} }
@ -262,19 +229,15 @@ limitations under the License. -->
watch( watch(
() => [ () => [
...(props.config.metricTypes || []),
...(props.config.metrics || []),
...(props.config.metricConfig || []), ...(props.config.metricConfig || []),
...(props.config.expressions || []), ...(props.config.expressions || []),
...(props.config.subExpressions || []), ...(props.config.subExpressions || []),
props.config.metricMode,
], ],
(data, old) => { (data, old) => {
if (JSON.stringify(data) === JSON.stringify(old)) { if (JSON.stringify(data) === JSON.stringify(old)) {
return; return;
} }
metricConfig.value = props.config.metricConfig; metricConfig.value = props.config.metricConfig;
metricMode.value = props.config.metricMode;
queryInstanceMetrics(instances.value); queryInstanceMetrics(instances.value);
}, },
); );

View File

@ -51,8 +51,7 @@ limitations under the License. -->
:config="{ :config="{
...config, ...config,
metricConfig, metricConfig,
metricTypes, typesOfMQE,
metricMode,
}" }"
v-if="colMetrics.length" v-if="colMetrics.length"
/> />
@ -78,9 +77,8 @@ limitations under the License. -->
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app"; import { useAppStoreWithOut } from "@/store/modules/app";
import type { Service } from "@/types/selector"; import type { Service } from "@/types/selector";
import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useMetricsProcessor";
import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor"; import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor";
import { EntityType, MetricModes } from "../data"; import { EntityType } from "../data";
import router from "@/router"; import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession"; import getDashboard from "@/hooks/useDashboardsSession";
import type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
@ -95,12 +93,9 @@ limitations under the License. -->
type: Object as PropType< type: Object as PropType<
ServiceListConfig & { ServiceListConfig & {
i: string; i: string;
metrics: string[];
metricTypes: string[];
isEdit: boolean; isEdit: boolean;
names: string[]; names: string[];
metricConfig: MetricConfigOpt[]; metricConfig: MetricConfigOpt[];
metricMode: string;
expressions: string[]; expressions: string[];
typesOfMQE: string[]; typesOfMQE: string[];
subExpressions: string[]; subExpressions: string[];
@ -125,8 +120,7 @@ limitations under the License. -->
const groups = ref<any>({}); const groups = ref<any>({});
const sortServices = ref<(Service & { merge: boolean })[]>([]); const sortServices = ref<(Service & { merge: boolean })[]>([]);
const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []); const metricConfig = ref<MetricConfigOpt[]>(props.config.metricConfig || []);
const metricTypes = ref<string[]>(props.config.metricTypes || []); const typesOfMQE = ref<string[]>(props.config.typesOfMQE || []);
const metricMode = ref<string>(props.config.metricMode);
queryServices(); queryServices();
@ -211,43 +205,7 @@ limitations under the License. -->
shortName: d.shortName, shortName: d.shortName,
}; };
}); });
if (props.config.metricMode === MetricModes.Expression) { queryServiceExpressions(currentServices);
queryServiceExpressions(currentServices);
return;
}
const metrics = props.config.metrics || [];
const types = props.config.metricTypes || [];
if (metrics.length && metrics[0] && types.length && types[0]) {
const params = await useQueryPodsMetrics(
currentServices,
{ ...props.config, metricConfig: metricConfig.value || [] },
EntityType[0].value,
);
const json = await dashboardStore.fetchMetricValue(params);
if (json.errors) {
ElMessage.error(json.errors);
return;
}
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;
colMetrics.value = [];
colMetrics.value = [];
metricTypes.value = [];
metricConfig.value = [];
} }
async function queryServiceExpressions(currentServices: Service[]) { async function queryServiceExpressions(currentServices: Service[]) {
const expressions = props.config.expressions || []; const expressions = props.config.expressions || [];
@ -262,16 +220,16 @@ limitations under the License. -->
services.value = params.data; services.value = params.data;
colMetrics.value = params.names; colMetrics.value = params.names;
colSubMetrics.value = params.subNames; colSubMetrics.value = params.subNames;
metricTypes.value = params.metricTypesArr;
metricConfig.value = params.metricConfigArr; metricConfig.value = params.metricConfigArr;
typesOfMQE.value = params.metricTypesArr;
emit("expressionTips", { tips: params.expressionsTips, subTips: params.subExpressionsTips }); emit("expressionTips", { tips: params.expressionsTips, subTips: params.subExpressionsTips });
return; return;
} }
services.value = currentServices; services.value = currentServices;
colMetrics.value = []; colMetrics.value = [];
colSubMetrics.value = []; colSubMetrics.value = [];
metricTypes.value = [];
metricConfig.value = []; metricConfig.value = [];
typesOfMQE.value = [];
emit("expressionTips", [], []); emit("expressionTips", [], []);
} }
function objectSpanMethod(param: any): any { function objectSpanMethod(param: any): any {
@ -306,19 +264,15 @@ limitations under the License. -->
watch( watch(
() => [ () => [
...(props.config.metricTypes || []),
...(props.config.metrics || []),
...(props.config.metricConfig || []), ...(props.config.metricConfig || []),
...(props.config.expressions || []), ...(props.config.expressions || []),
...(props.config.subExpressions || []), ...(props.config.subExpressions || []),
props.config.metricMode,
], ],
(data, old) => { (data, old) => {
if (JSON.stringify(data) === JSON.stringify(old)) { if (JSON.stringify(data) === JSON.stringify(old)) {
return; return;
} }
metricConfig.value = props.config.metricConfig; metricConfig.value = props.config.metricConfig;
metricMode.value = props.config.metricMode;
queryServiceMetrics(services.value); queryServiceMetrics(services.value);
}, },
); );

View File

@ -56,7 +56,6 @@ limitations under the License. -->
type: Object as PropType<{ type: Object as PropType<{
showTableValues: boolean; showTableValues: boolean;
tableHeaderCol2: string; tableHeaderCol2: string;
metricTypes: string[];
typesOfMQE: string[]; typesOfMQE: string[];
}>, }>,
default: () => ({ showTableValues: true }), default: () => ({ showTableValues: true }),
@ -66,10 +65,6 @@ limitations under the License. -->
const { t } = useI18n(); const { t } = useI18n();
const nameWidth = computed(() => (props.config.showTableValues ? 80 : 100)); const nameWidth = computed(() => (props.config.showTableValues ? 80 : 100));
const dataKeys = computed(() => { const dataKeys = computed(() => {
if (props.config.metricTypes && props.config.metricTypes[0] === "readMetricsValue") {
const keys = Object.keys(props.data || {});
return keys;
}
const keys = Object.keys(props.data || {}).filter( const keys = Object.keys(props.data || {}).filter(
(i: string) => Array.isArray(props.data[i]) && props.data[i].length, (i: string) => Array.isArray(props.data[i]) && props.data[i].length,
); );

View File

@ -64,8 +64,7 @@ limitations under the License. -->
import copy from "@/utils/copy"; import copy from "@/utils/copy";
import { TextColors } from "@/views/dashboard/data"; import { TextColors } from "@/views/dashboard/data";
import Trace from "@/views/dashboard/related/trace/Index.vue"; import Trace from "@/views/dashboard/related/trace/Index.vue";
import { QueryOrders, Status, RefIdTypes, ProtocolTypes, ExpressionResultType } from "../data"; import { WidgetType, QueryOrders, Status, RefIdTypes, ExpressionResultType } from "@/views/dashboard/data";
import { WidgetType } from "@/views/dashboard/data";
/*global defineProps */ /*global defineProps */
const props = defineProps({ const props = defineProps({
@ -77,10 +76,8 @@ limitations under the License. -->
}, },
config: { config: {
type: Object as PropType<{ type: Object as PropType<{
metricMode: string;
color: string; color: string;
metrics: string[]; expressions: string[];
metricTypes: string[];
typesOfMQE: string[]; typesOfMQE: string[];
relatedTrace: any; relatedTrace: any;
}>, }>,
@ -116,11 +113,8 @@ limitations under the License. -->
queryOrder: QueryOrders[1].value, queryOrder: QueryOrders[1].value,
status: Status[2].value, status: Status[2].value,
id: item.refId, id: item.refId,
metricValue: [{ label: props.config.metrics[0], data: item.value, value: item.name }], metricValue: [{ label: props.config.expressions[0], data: item.value, value: item.name }],
isReadRecords: isReadRecords: props.config.typesOfMQE.includes(ExpressionResultType.RECORD_LIST) || undefined,
props.config.typesOfMQE.includes(ExpressionResultType.RECORD_LIST) ||
props.config.metricTypes.includes(ProtocolTypes.ReadRecords) ||
undefined,
}; };
traceOptions.value = { traceOptions.value = {
...traceOptions.value, ...traceOptions.value,

View File

@ -22,23 +22,7 @@ limitations under the License. -->
> >
<template #default="scope"> <template #default="scope">
<div class="chart"> <div class="chart">
<Line <span class="item flex-h">
v-if="useListConfig(config, index).isLinear && config.metricMode !== MetricModes.Expression"
:data="{
[metric]: scope.row[metric] && scope.row[metric].values,
}"
:intervalTime="intervalTime"
:config="{
showXAxis: false,
showYAxis: false,
smallTips: true,
showlabels: false,
}"
/>
<span
class="item flex-h"
v-else-if="useListConfig(config, index).isAvg || config.metricMode === MetricModes.Expression"
>
<el-popover placement="left" :width="400" trigger="click"> <el-popover placement="left" :width="400" trigger="click">
<template #reference> <template #reference>
<span class="trend"> <span class="trend">
@ -70,7 +54,6 @@ limitations under the License. -->
/> />
</span> </span>
</span> </span>
<Card v-else :data="{ [metric]: scope.row[metric] }" :config="{ textAlign: 'left' }" />
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -79,11 +62,9 @@ limitations under the License. -->
<script lang="ts" setup> <script lang="ts" setup>
import type { PropType } from "vue"; import type { PropType } from "vue";
import type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
import { useListConfig } from "@/hooks/useListConfig";
import Line from "../Line.vue"; import Line from "../Line.vue";
import Card from "../Card.vue"; import Card from "../Card.vue";
import { MetricQueryTypes } from "@/hooks/data"; import { ExpressionResultType } from "@/views/dashboard/data";
import { ExpressionResultType, MetricModes } from "@/views/dashboard/data";
/*global defineProps */ /*global defineProps */
const props = defineProps({ const props = defineProps({
@ -92,9 +73,8 @@ limitations under the License. -->
config: { config: {
type: Object as PropType<{ type: Object as PropType<{
i: string; i: string;
metricTypes: string[]; typesOfMQE: string[];
metricConfig: MetricConfigOpt[]; metricConfig: MetricConfigOpt[];
metricMode: string;
}>, }>,
default: () => ({}), default: () => ({}),
}, },
@ -120,13 +100,9 @@ limitations under the License. -->
} }
if (label) { if (label) {
if ( if (
( ([ExpressionResultType.TIME_SERIES_VALUES, ExpressionResultType.SINGLE_VALUE] as string[]).includes(
[ props.config.typesOfMQE[i],
MetricQueryTypes.ReadLabeledMetricsValues, )
ExpressionResultType.TIME_SERIES_VALUES,
ExpressionResultType.SINGLE_VALUE,
] as string[]
).includes(props.config.metricTypes[i])
) { ) {
const name = (label || "").split(",").map((item: string) => item.replace(/^\s*|\s*$/g, ""))[ const name = (label || "").split(",").map((item: string) => item.replace(/^\s*|\s*$/g, ""))[
props.config.metricConfig[i].index || 0 props.config.metricConfig[i].index || 0

View File

@ -19,7 +19,7 @@ limitations under the License. -->
{{ t("expressions") }} {{ t("expressions") }}
</span> </span>
<span class="label" v-else> <span class="label" v-else>
{{ t(dashboardStore.selectedGrid.metricMode === MetricModes.General ? "metrics" : "expressions") }} {{ t("expressions") }}
</span> </span>
<SelectSingle :value="currentMetric" :options="metricList" @change="changeMetric" class="selectors" /> <SelectSingle :value="currentMetric" :options="metricList" @change="changeMetric" class="selectors" />
</div> </div>
@ -43,33 +43,19 @@ limitations under the License. -->
@change="changeConfigs({ label: currentConfig.label })" @change="changeConfigs({ label: currentConfig.label })"
/> />
</div> </div>
<div
class="item mb-10"
v-if="type !== 'hierarchyServicesConfig' && dashboardStore.selectedGrid.metricMode === MetricModes.General"
>
<span class="label">{{ t("aggregation") }}</span>
<SelectSingle
:value="currentConfig.calculation"
:options="CalculationOpts"
@change="changeConfigs({ calculation: $event })"
class="selectors"
:clearable="true"
/>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch } from "vue"; import { ref, computed, watch } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import type { Option } from "@/types/app"; import type { Option } from "@/types/app";
import { CalculationOpts, MetricModes, EntityType, ConfigFieldTypes } from "@/views/dashboard/data"; import { EntityType, ConfigFieldTypes } from "@/views/dashboard/data";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import getDashboard from "@/hooks/useDashboardsSession"; import getDashboard from "@/hooks/useDashboardsSession";
/*global defineEmits, defineProps */ /*global defineEmits, defineProps */
const props = defineProps({ const props = defineProps({
type: { type: String, default: "" }, type: { type: String, default: "" },
isExpression: { type: Boolean, default: true },
layer: { type: String, default: "" }, layer: { type: String, default: "" },
expressions: { type: Array<string>, default: () => [] }, expressions: { type: Array<string>, default: () => [] },
entity: { type: String, default: EntityType[0].value }, entity: { type: String, default: EntityType[0].value },
@ -82,23 +68,16 @@ limitations under the License. -->
return props.expressions || []; return props.expressions || [];
} }
let metrics: string[] = []; let metrics: string[] = [];
const { const { linkServerExpressions, linkClientExpressions, nodeExpressions } = dashboardStore.selectedGrid;
linkServerExpressions,
linkServerMetrics,
linkClientExpressions,
linkClientMetrics,
nodeExpressions,
nodeMetrics,
} = dashboardStore.selectedGrid;
switch (props.type) { switch (props.type) {
case "linkServerMetricConfig": case "linkServerMetricConfig":
metrics = props.isExpression ? linkServerExpressions : linkServerMetrics; metrics = linkServerExpressions;
break; break;
case "linkClientMetricConfig": case "linkClientMetricConfig":
metrics = props.isExpression ? linkClientExpressions : linkClientMetrics; metrics = linkClientExpressions;
break; break;
case "nodeMetricConfig": case "nodeMetricConfig":
metrics = props.isExpression ? nodeExpressions : nodeMetrics; metrics = nodeExpressions;
break; break;
} }
return metrics || []; return metrics || [];

View File

@ -13,17 +13,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div class="mt-20">
<h5 class="title">{{ t("metricMode") }}</h5>
<el-switch
v-model="isExpression"
class="mt-5"
active-text="Expressions"
inactive-text="General"
size="small"
@change="changeMetricMode"
/>
</div>
<div class="mb-20"> <div class="mb-20">
<h5 class="title">{{ t("callSettings") }}</h5> <h5 class="title">{{ t("callSettings") }}</h5>
<div class="label">{{ t("linkDashboard") }}</div> <div class="label">{{ t("linkDashboard") }}</div>
@ -38,21 +27,16 @@ limitations under the License. -->
/> />
<div class="label"> <div class="label">
<span>{{ t("linkServerMetrics") }}</span> <span>{{ t("linkServerMetrics") }}</span>
<el-popover <el-popover placement="left" :width="400" trigger="click" v-if="states.linkServerExpressions.length">
placement="left"
:width="400"
trigger="click"
v-if="isExpression ? states.linkServerExpressions.length : states.linkServerMetrics.length"
>
<template #reference> <template #reference>
<span @click="setConfigType('linkServerMetricConfig')"> <span @click="setConfigType('linkServerMetricConfig')">
<Icon class="cp ml-5" iconName="mode_edit" size="middle" /> <Icon class="cp ml-5" iconName="mode_edit" size="middle" />
</span> </span>
</template> </template>
<Metrics :type="configType" :isExpression="isExpression" @update="updateSettings" /> <Metrics :type="configType" @update="updateSettings" />
</el-popover> </el-popover>
</div> </div>
<div v-if="isExpression"> <div>
<Tags <Tags
:tags="states.linkServerExpressions" :tags="states.linkServerExpressions"
:vertical="true" :vertical="true"
@ -60,34 +44,19 @@ limitations under the License. -->
@change="(param: string[]) => changeLinkServerExpressions(param)" @change="(param: string[]) => changeLinkServerExpressions(param)"
/> />
</div> </div>
<Selector
v-else
class="inputs"
:multiple="true"
:value="states.linkServerMetrics"
:options="states.linkMetricList"
size="small"
placeholder="Select metrics"
@change="updateLinkServerMetrics"
/>
<span v-show="dashboardStore.entity !== EntityType[2].value"> <span v-show="dashboardStore.entity !== EntityType[2].value">
<div class="label"> <div class="label">
<span>{{ t("linkClientMetrics") }}</span> <span>{{ t("linkClientMetrics") }}</span>
<el-popover <el-popover placement="left" :width="400" trigger="click" v-if="states.linkClientExpressions.length">
placement="left"
:width="400"
trigger="click"
v-if="isExpression ? states.linkClientExpressions.length : states.linkClientMetrics.length"
>
<template #reference> <template #reference>
<span @click="setConfigType('linkClientMetricConfig')"> <span @click="setConfigType('linkClientMetricConfig')">
<Icon class="cp ml-5" iconName="mode_edit" size="middle" /> <Icon class="cp ml-5" iconName="mode_edit" size="middle" />
</span> </span>
</template> </template>
<Metrics :type="configType" :isExpression="isExpression" @update="updateSettings" /> <Metrics :type="configType" @update="updateSettings" />
</el-popover> </el-popover>
</div> </div>
<div v-if="isExpression"> <div>
<Tags <Tags
:tags="states.linkClientExpressions" :tags="states.linkClientExpressions"
:vertical="true" :vertical="true"
@ -95,16 +64,6 @@ limitations under the License. -->
@change="(param: string[]) => changeLinkClientExpressions(param)" @change="(param: string[]) => changeLinkClientExpressions(param)"
/> />
</div> </div>
<Selector
v-else
class="inputs"
:multiple="true"
:value="states.linkClientMetrics"
:options="states.linkMetricList"
size="small"
placeholder="Select metrics"
@change="updateLinkClientMetrics"
/>
</span> </span>
</div> </div>
<div> <div>
@ -149,21 +108,16 @@ limitations under the License. -->
</div> </div>
<div class="label"> <div class="label">
<span>{{ t("nodeMetrics") }}</span> <span>{{ t("nodeMetrics") }}</span>
<el-popover <el-popover placement="left" :width="400" trigger="click" v-if="states.nodeExpressions.length">
placement="left"
:width="400"
trigger="click"
v-if="isExpression ? states.nodeExpressions.length : states.nodeMetrics.length"
>
<template #reference> <template #reference>
<span @click="setConfigType('nodeMetricConfig')"> <span @click="setConfigType('nodeMetricConfig')">
<Icon class="cp ml-5" iconName="mode_edit" size="middle" /> <Icon class="cp ml-5" iconName="mode_edit" size="middle" />
</span> </span>
</template> </template>
<Metrics :type="configType" :isExpression="isExpression" @update="updateSettings" /> <Metrics :type="configType" @update="updateSettings" />
</el-popover> </el-popover>
</div> </div>
<div v-if="isExpression"> <div>
<Tags <Tags
:tags="states.nodeExpressions" :tags="states.nodeExpressions"
:vertical="true" :vertical="true"
@ -171,28 +125,17 @@ limitations under the License. -->
@change="(param: string[]) => changeNodeExpressions(param)" @change="(param: string[]) => changeNodeExpressions(param)"
/> />
</div> </div>
<Selector
v-else
class="inputs"
:multiple="true"
:value="states.nodeMetrics"
:options="states.nodeMetricList"
size="small"
placeholder="Select metrics"
@change="updateNodeMetrics"
/>
</div> </div>
<div v-show="isService"> <div v-show="isService">
<h5 class="title">{{ t("legendSettings") }}</h5> <h5 class="title">{{ t("legendSettings") }}</h5>
<span v-if="isExpression"> <span>
<div class="label">Healthy Description</div> <div class="label">Healthy Description</div>
<el-input v-model="description.healthy" placeholder="Please input description" size="small" class="mt-5" /> <el-input v-model="description.healthy" placeholder="Please input description" size="small" class="mt-5" />
</span> </span>
<div class="label"> <div class="label">
<span>{{ t(isExpression ? "unhealthyExpression" : "conditions") }}</span> <span>{{ t("unhealthyExpression") }}</span>
<el-tooltip <el-tooltip
class="cp" class="cp"
v-if="isExpression"
content="The node would be red to indicate unhealthy status when the expression return greater than 0" content="The node would be red to indicate unhealthy status when the expression return greater than 0"
> >
<span> <span>
@ -200,50 +143,9 @@ limitations under the License. -->
</span> </span>
</el-tooltip> </el-tooltip>
</div> </div>
<div v-if="isExpression"> <div>
<el-input v-model="legendMQE.expression" placeholder="Please input a expression" size="small" class="inputs" /> <el-input v-model="legendMQE.expression" placeholder="Please input a expression" size="small" class="inputs" />
</div> </div>
<div v-for="(metric, index) of legend" :key="index" v-else>
<Selector
class="item"
:value="metric.name"
:options="states.nodeMetricList"
size="small"
placeholder="Select a metric"
@change="changeLegend(LegendOpt.NAME, $event, index)"
/>
<Selector
class="input-small"
:value="metric.condition"
:options="MetricConditions"
size="small"
placeholder="Select a condition"
@change="changeLegend(LegendOpt.CONDITION, $event, index)"
/>
<el-input
v-model="metric.value"
placeholder="Please input a value"
type="number"
@change="changeLegend(LegendOpt.VALUE, $event, index)"
size="small"
class="item"
/>
<span>
<Icon class="cp delete" iconName="remove_circle_outline" size="middle" @click="deleteMetric(index)" />
<Icon
class="cp"
iconName="add_circle_outlinecontrol_point"
size="middle"
v-show="index === legend.length - 1 && legend.length < 5"
@click="addMetric"
/>
</span>
<div v-show="index !== legend.length - 1">&&</div>
</div>
<span v-if="!isExpression">
<div class="label">Healthy Description</div>
<el-input v-model="description.healthy" placeholder="Please input description" size="small" class="mt-5" />
</span>
<div class="label">Unhealthy Description</div> <div class="label">Unhealthy Description</div>
<el-input v-model="description.unhealthy" placeholder="Please input description" size="small" class="mt-5" /> <el-input v-model="description.unhealthy" placeholder="Please input description" size="small" class="mt-5" />
<el-button @click="setLegend" class="mt-20" size="small" type="primary"> <el-button @click="setLegend" class="mt-20" size="small" type="primary">
@ -257,20 +159,9 @@ limitations under the License. -->
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { useTopologyStore } from "@/store/modules/topology"; import { useTopologyStore } from "@/store/modules/topology";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { import { ScopeType, EntityType, CallTypes } from "@/views/dashboard/data";
MetricCatalog,
ScopeType,
MetricConditions,
EntityType,
LegendOpt,
MetricsType,
MetricModes,
CallTypes,
} from "@/views/dashboard/data";
import type { Option } from "@/types/app"; import type { Option } from "@/types/app";
import { useQueryTopologyMetrics } from "@/hooks/useMetricsProcessor";
import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor"; import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor";
import type { Node } from "@/types/topology";
import type { DashboardItem, MetricConfigOpt } from "@/types/dashboard"; import type { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
import Metrics from "./Metrics.vue"; import Metrics from "./Metrics.vue";
@ -280,7 +171,6 @@ limitations under the License. -->
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const topologyStore = useTopologyStore(); const topologyStore = useTopologyStore();
const { selectedGrid } = dashboardStore; const { selectedGrid } = dashboardStore;
const isExpression = ref<boolean>(dashboardStore.selectedGrid.metricMode === MetricModes.Expression);
const nodeDashboard = const nodeDashboard =
selectedGrid.nodeDashboard && selectedGrid.nodeDashboard.length ? selectedGrid.nodeDashboard : ""; selectedGrid.nodeDashboard && selectedGrid.nodeDashboard.length ? selectedGrid.nodeDashboard : "";
const isService = [EntityType[0].value, EntityType[1].value].includes(dashboardStore.entity); const isService = [EntityType[0].value, EntityType[1].value].includes(dashboardStore.entity);
@ -296,11 +186,6 @@ limitations under the License. -->
scope: string; scope: string;
dashboard: string; dashboard: string;
}[]; }[];
linkServerMetrics: string[];
linkClientMetrics: string[];
nodeMetrics: string[];
nodeMetricList: Option[];
linkMetricList: Option[];
linkDashboards: (DashboardItem & { label: string; value: string })[]; linkDashboards: (DashboardItem & { label: string; value: string })[];
nodeDashboards: (DashboardItem & { label: string; value: string })[]; nodeDashboards: (DashboardItem & { label: string; value: string })[];
linkServerExpressions: string[]; linkServerExpressions: string[];
@ -309,27 +194,18 @@ limitations under the License. -->
}>({ }>({
linkDashboard: selectedGrid.linkDashboard || "", linkDashboard: selectedGrid.linkDashboard || "",
nodeDashboard: selectedGrid.nodeDashboard || [], nodeDashboard: selectedGrid.nodeDashboard || [],
linkServerMetrics: selectedGrid.linkServerMetrics || [],
linkClientMetrics: selectedGrid.linkClientMetrics || [],
nodeMetrics: selectedGrid.nodeMetrics || [],
nodeMetricList: [],
linkMetricList: [],
linkDashboards: [], linkDashboards: [],
nodeDashboards: [], nodeDashboards: [],
linkServerExpressions: selectedGrid.linkServerExpressions || [], linkServerExpressions: selectedGrid.linkServerExpressions || [],
linkClientExpressions: selectedGrid.linkClientExpressions || [], linkClientExpressions: selectedGrid.linkClientExpressions || [],
nodeExpressions: selectedGrid.nodeExpressions || [], nodeExpressions: selectedGrid.nodeExpressions || [],
}); });
const l = selectedGrid.legend && selectedGrid.legend.length;
const legend = ref<{ name: string; condition: string; value: string }[]>(
l ? selectedGrid.legend : [{ name: "", condition: "", value: "" }],
);
const legendMQE = ref<{ expression: string }>(selectedGrid.legendMQE || { expression: "" }); const legendMQE = ref<{ expression: string }>(selectedGrid.legendMQE || { expression: "" });
const configType = ref<string>(""); const configType = ref<string>("");
const description = reactive<any>(selectedGrid.description || {}); const description = reactive<any>(selectedGrid.description || {});
getMetricList(); getDashboardList();
async function getMetricList() { async function getDashboardList() {
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]"); const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
const json = await dashboardStore.fetchMetricList(); const json = await dashboardStore.fetchMetricList();
if (json.errors) { if (json.errors) {
@ -351,13 +227,6 @@ limitations under the License. -->
}, },
[], [],
); );
states.nodeMetricList = (json.data.metrics || []).filter(
(d: { type: string }) => d.type === MetricsType.REGULAR_VALUE,
);
states.linkMetricList = (json.data.metrics || []).filter(
(d: { catalog: string; type: string }) =>
entity + "Relation" === (MetricCatalog as any)[d.catalog] && d.type === MetricsType.REGULAR_VALUE,
);
if (isService) { if (isService) {
return; return;
} }
@ -373,36 +242,15 @@ limitations under the License. -->
} }
async function setLegend() { async function setLegend() {
updateSettings(); updateSettings();
if (isExpression.value) { const expression = dashboardStore.selectedGrid.legendMQE && dashboardStore.selectedGrid.legendMQE.expression;
const expression = dashboardStore.selectedGrid.legendMQE && dashboardStore.selectedGrid.legendMQE.expression; const { getExpressionQuery } = useQueryTopologyExpressionsProcessor([expression], topologyStore.nodes);
if (!expression) { const param = getExpressionQuery();
emit("updateNodes"); const res = await topologyStore.getNodeExpressionValue(param);
return; if (res.errors) {
} ElMessage.error(res.errors);
const { getExpressionQuery } = useQueryTopologyExpressionsProcessor([expression], topologyStore.nodes);
const param = getExpressionQuery();
const res = await topologyStore.getNodeExpressionValue(param);
if (res.errors) {
ElMessage.error(res.errors);
} else {
topologyStore.setLegendValues([expression], res.data);
}
} else { } else {
const names = dashboardStore.selectedGrid.legend.map((d: any) => d.name && d.condition && d.value); topologyStore.setLegendValues([expression], res.data);
if (!names.length) {
emit("updateNodes");
return;
}
const ids = topologyStore.nodes.map((d: Node) => d.id);
const param = await useQueryTopologyMetrics(names, ids);
const res = await topologyStore.getLegendMetrics(param);
if (res.errors) {
ElMessage.error(res.errors);
}
} }
emit("updateNodes");
} }
function changeNodeDashboard(opt: any) { function changeNodeDashboard(opt: any) {
states.nodeDashboard = opt[0].value; states.nodeDashboard = opt[0].value;
@ -412,9 +260,6 @@ limitations under the License. -->
states.linkDashboard = opt[0].value; states.linkDashboard = opt[0].value;
updateSettings(); updateSettings();
} }
function changeLegend(type: string, opt: any, index: number) {
(legend.value[index] as any)[type] = opt[0].value || opt;
}
function changeScope(index: number, opt: Option[] | any) { function changeScope(index: number, opt: Option[] | any) {
items[index].scope = opt[0].value; items[index].scope = opt[0].value;
const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]"); const list = JSON.parse(sessionStorage.getItem("dashboards") || "[]");
@ -445,27 +290,15 @@ limitations under the License. -->
updateSettings(); updateSettings();
} }
function updateSettings(metricConfig?: { [key: string]: MetricConfigOpt[] }) { function updateSettings(metricConfig?: { [key: string]: MetricConfigOpt[] }) {
let metrics = [];
if (isExpression.value) {
metrics = legend.value.filter((d: any) => d.name);
} else {
metrics = legend.value.filter((d: any) => d.name && d.value && d.condition);
}
const param = { const param = {
...dashboardStore.selectedGrid, ...dashboardStore.selectedGrid,
linkDashboard: states.linkDashboard, linkDashboard: states.linkDashboard,
nodeDashboard: isService nodeDashboard: isService
? items.filter((d: { scope: string; dashboard: string }) => d.dashboard) ? items.filter((d: { scope: string; dashboard: string }) => d.dashboard)
: states.nodeDashboard, : states.nodeDashboard,
linkServerMetrics: states.linkServerMetrics,
linkClientMetrics: states.linkClientMetrics,
nodeMetrics: states.nodeMetrics,
linkServerExpressions: states.linkServerExpressions, linkServerExpressions: states.linkServerExpressions,
linkClientExpressions: states.linkClientExpressions, linkClientExpressions: states.linkClientExpressions,
nodeExpressions: states.nodeExpressions, nodeExpressions: states.nodeExpressions,
metricMode: isExpression.value ? MetricModes.Expression : MetricModes.General,
legend: metrics,
legendMQE: legendMQE.value, legendMQE: legendMQE.value,
...metricConfig, ...metricConfig,
description, description,
@ -474,30 +307,8 @@ limitations under the License. -->
dashboardStore.setConfigs(param); dashboardStore.setConfigs(param);
emit("update", param); emit("update", param);
} }
function updateLinkServerMetrics(options: Option[] | any) {
const opt = options.map((d: Option) => d.value);
const index = states.linkServerMetrics.findIndex((d: any) => !opt.includes(d));
states.linkServerMetrics = opt;
if (index < 0) {
changeLinkServerMetrics();
return;
}
const origin = dashboardStore.selectedGrid.linkServerMetricConfig || [];
const config = origin.length === 1 ? [] : origin.splice(index, 1);
changeLinkServerMetrics({ linkServerMetricConfig: config });
}
async function changeLinkServerMetrics(config?: { [key: string]: MetricConfigOpt[] }) {
updateSettings(config);
if (!states.linkServerMetrics.length) {
topologyStore.setLinkServerMetrics({});
return;
}
topologyStore.getLinkServerMetrics(states.linkServerMetrics);
}
function changeLinkServerExpressions(param: string[]) { function changeLinkServerExpressions(param: string[]) {
if (!isExpression.value) {
return;
}
states.linkServerExpressions = param; states.linkServerExpressions = param;
updateSettings(); updateSettings();
if (!states.linkServerExpressions.length) { if (!states.linkServerExpressions.length) {
@ -507,9 +318,6 @@ limitations under the License. -->
topologyStore.getLinkExpressions(states.linkServerExpressions, CallTypes.Server); topologyStore.getLinkExpressions(states.linkServerExpressions, CallTypes.Server);
} }
function changeLinkClientExpressions(param: string[]) { function changeLinkClientExpressions(param: string[]) {
if (!isExpression.value) {
return;
}
states.linkClientExpressions = param; states.linkClientExpressions = param;
updateSettings(); updateSettings();
if (!states.linkClientExpressions.length) { if (!states.linkClientExpressions.length) {
@ -518,63 +326,11 @@ limitations under the License. -->
} }
topologyStore.getLinkExpressions(states.linkClientExpressions, CallTypes.Client); topologyStore.getLinkExpressions(states.linkClientExpressions, CallTypes.Client);
} }
function updateLinkClientMetrics(options: Option[] | any) {
const opt = options.map((d: Option) => d.value);
const index = states.linkClientMetrics.findIndex((d: any) => !opt.includes(d));
states.linkClientMetrics = opt;
if (index < 0) {
changeLinkClientMetrics();
return;
}
const origin = dashboardStore.selectedGrid.linkClientMetricConfig || [];
const config = origin.length === 1 ? [] : origin.splice(index, 1);
changeLinkClientMetrics({ linkClientMetricConfig: config });
}
async function changeLinkClientMetrics(config?: { [key: string]: MetricConfigOpt[] }) {
updateSettings(config);
if (!states.linkClientMetrics.length) {
topologyStore.setLinkClientMetrics({});
return;
}
topologyStore.getLinkClientMetrics(states.linkClientMetrics);
}
function updateNodeMetrics(options: Option[] | any) {
const opt = options.map((d: Option) => d.value);
const index = states.nodeMetrics.findIndex((d: any) => !opt.includes(d));
states.nodeMetrics = opt;
if (index < 0) {
changeNodeMetrics();
return;
}
const origin = dashboardStore.selectedGrid.nodeMetricConfig || [];
const config = origin.length === 1 ? [] : origin.splice(index, 1);
changeNodeMetrics({ nodeMetricConfig: config });
}
async function changeNodeMetrics(config?: { [key: string]: MetricConfigOpt[] }) {
updateSettings(config);
if (!states.nodeMetrics.length) {
topologyStore.setNodeMetricValue({});
return;
}
topologyStore.queryNodeMetrics(states.nodeMetrics);
}
function deleteMetric(index: number) {
if (legend.value.length === 1) {
legend.value = [{ name: "", condition: "", value: "" }];
return;
}
legend.value.splice(index, 1);
}
function addMetric() {
legend.value.push({ name: "", condition: "", value: "" });
}
function setConfigType(type: string) { function setConfigType(type: string) {
configType.value = type; configType.value = type;
} }
function changeNodeExpressions(param: string[]) { function changeNodeExpressions(param: string[]) {
if (!isExpression.value) {
return;
}
states.nodeExpressions = param; states.nodeExpressions = param;
updateSettings(); updateSettings();
if (!states.nodeExpressions.length) { if (!states.nodeExpressions.length) {
@ -583,25 +339,6 @@ limitations under the License. -->
} }
topologyStore.queryNodeExpressions(states.nodeExpressions); topologyStore.queryNodeExpressions(states.nodeExpressions);
} }
function changeMetricMode() {
legend.value = [{ name: "", condition: "", value: "" }];
const config = {
linkServerMetricConfig: [],
linkClientMetricConfig: [],
nodeMetricConfig: [],
};
if (isExpression.value) {
states.linkServerMetrics = [];
states.linkClientMetrics = [];
states.nodeMetrics = [];
} else {
states.linkServerExpressions = [];
states.linkClientExpressions = [];
states.nodeExpressions = [];
}
updateSettings(config);
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.inputs { .inputs {

View File

@ -41,7 +41,6 @@ limitations under the License. -->
import router from "@/router"; import router from "@/router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
import { aggregation } from "@/hooks/useMetricsProcessor";
import getDashboard from "@/hooks/useDashboardsSession"; import getDashboard from "@/hooks/useDashboardsSession";
import Graph from "../components/Graph.vue"; import Graph from "../components/Graph.vue";
@ -129,8 +128,8 @@ limitations under the License. -->
(val: { id: string; value: unknown }) => val.id === data.id, (val: { id: string; value: unknown }) => val.id === data.id,
) || {}; ) || {};
const opt: MetricConfigOpt = nodeMetricConfig[index] || {}; const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
const v = aggregation(metric.value, opt);
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${v} ${opt.unit || ""}</div>`; return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${opt.unit || ""}</div>`;
}); });
const tipHtml = [ const tipHtml = [
`<div class="mb-5"><span class="grey">name: </span>${data.name}</div><div class="mb-5"><span class="grey">layer: </span>${data.layer}</div>`, `<div class="mb-5"><span class="grey">name: </span>${data.name}</div><div class="mb-5"><span class="grey">layer: </span>${data.layer}</div>`,

View File

@ -69,7 +69,7 @@ limitations under the License. -->
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 { EntityType, DepthList, MetricModes, CallTypes } from "@/views/dashboard/data"; import { EntityType, DepthList, CallTypes } from "@/views/dashboard/data";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import Sankey from "./Sankey.vue"; import Sankey from "./Sankey.vue";
import Settings from "../config/Settings.vue"; import Settings from "../config/Settings.vue";
@ -119,15 +119,9 @@ limitations under the License. -->
}; };
height.value = dom.height - 70; height.value = dom.height - 70;
width.value = dom.width - 5; width.value = dom.width - 5;
if (settings.value.metricMode === MetricModes.Expression) { topologyStore.queryNodeExpressions(settings.value.nodeExpressions || []);
topologyStore.queryNodeExpressions(settings.value.nodeExpressions || []); topologyStore.getLinkExpressions(settings.value.linkClientExpressions || [], CallTypes.Client);
topologyStore.getLinkExpressions(settings.value.linkClientExpressions || [], CallTypes.Client); topologyStore.getLinkExpressions(settings.value.linkServerExpressions || [], CallTypes.Server);
topologyStore.getLinkExpressions(settings.value.linkServerExpressions || [], CallTypes.Server);
} else {
topologyStore.getLinkClientMetrics(settings.value.linkClientMetrics || []);
topologyStore.getLinkServerMetrics(settings.value.linkServerMetrics || []);
topologyStore.queryNodeMetrics(settings.value.nodeMetrics || []);
}
} }
function resize() { function resize() {

View File

@ -22,8 +22,6 @@ limitations under the License. -->
import { useTopologyStore } from "@/store/modules/topology"; import { useTopologyStore } from "@/store/modules/topology";
import type { Node, Call } from "@/types/topology"; import type { Node, Call } from "@/types/topology";
import type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
import { aggregation } from "@/hooks/useMetricsProcessor";
import { MetricModes } from "@/views/dashboard/data";
import { useAppStoreWithOut } from "@/store/modules/app"; import { useAppStoreWithOut } from "@/store/modules/app";
import { Themes } from "@/constants/data"; import { Themes } from "@/constants/data";
@ -85,14 +83,8 @@ limitations under the License. -->
}; };
} }
function linkTooltip(data: Call) { function linkTooltip(data: Call) {
const clientMetrics: string[] = const clientMetrics: string[] = props.settings.linkClientExpressions;
props.settings.metricMode === MetricModes.Expression const serverMetrics: string[] = props.settings.linkServerExpressions;
? props.settings.linkClientExpressions
: props.settings.linkClientMetrics;
const serverMetrics: string[] =
props.settings.metricMode === MetricModes.Expression
? props.settings.linkServerExpressions
: props.settings.linkServerMetrics;
const linkServerMetricConfig: MetricConfigOpt[] = props.settings.linkServerMetricConfig || []; const linkServerMetricConfig: MetricConfigOpt[] = props.settings.linkServerMetricConfig || [];
const linkClientMetricConfig: MetricConfigOpt[] = props.settings.linkClientMetricConfig || []; const linkClientMetricConfig: MetricConfigOpt[] = props.settings.linkClientMetricConfig || [];
@ -102,8 +94,10 @@ limitations under the License. -->
{}; {};
if (metric) { if (metric) {
const opt: MetricConfigOpt = linkServerMetricConfig[index] || {}; const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
const v = aggregation(metric.value, opt);
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${v} ${opt.unit || ""}</div>`; return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${
opt.unit || ""
}</div>`;
} }
}); });
const htmlClient = clientMetrics.map((m, index) => { const htmlClient = clientMetrics.map((m, index) => {
@ -111,8 +105,8 @@ limitations under the License. -->
const metric = const metric =
topologyStore.linkClientMetrics[m].values.find((val: { id: string; value: unknown }) => val.id === data.id) || topologyStore.linkClientMetrics[m].values.find((val: { id: string; value: unknown }) => val.id === data.id) ||
{}; {};
const v = aggregation(metric.value, opt);
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${v} ${opt.unit || ""}</div>`; return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${opt.unit || ""}</div>`;
}); });
const html = [ const html = [
`<div>${data.sourceObj.serviceName} -> ${data.targetObj.serviceName}</div>`, `<div>${data.sourceObj.serviceName} -> ${data.targetObj.serviceName}</div>`,
@ -124,17 +118,14 @@ limitations under the License. -->
} }
function nodeTooltip(data: Node) { function nodeTooltip(data: Node) {
const nodeMetrics: string[] = const nodeMetrics: string[] = props.settings.nodeExpressions;
props.settings.metricMode === MetricModes.Expression
? props.settings.nodeExpressions
: props.settings.nodeMetrics;
const nodeMetricConfig = props.settings.nodeMetricConfig || []; const nodeMetricConfig = props.settings.nodeMetricConfig || [];
const html = nodeMetrics.map((m, index) => { const html = nodeMetrics.map((m, index) => {
const metric = const metric =
topologyStore.nodeMetricValue[m].values.find((val: { id: string; value: unknown }) => val.id === data.id) || {}; topologyStore.nodeMetricValue[m].values.find((val: { id: string; value: unknown }) => val.id === data.id) || {};
const opt: MetricConfigOpt = nodeMetricConfig[index] || {}; const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
const v = aggregation(metric.value, opt);
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${v} ${opt.unit || ""}</div>`; return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${opt.unit || ""}</div>`;
}); });
return [` <div><span>name: </span>${data.serviceName}</div>`, ...html].join(" "); return [` <div><span>name: </span>${data.serviceName}</div>`, ...html].join(" ");
} }

View File

@ -43,7 +43,6 @@ limitations under the License. -->
import router from "@/router"; import router from "@/router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
import { aggregation } from "@/hooks/useMetricsProcessor";
import getDashboard from "@/hooks/useDashboardsSession"; import getDashboard from "@/hooks/useDashboardsSession";
import Graph from "../components/Graph.vue"; import Graph from "../components/Graph.vue";
@ -137,8 +136,7 @@ limitations under the License. -->
(val: { id: string; value: unknown }) => val.id === data.id, (val: { id: string; value: unknown }) => val.id === data.id,
) || {}; ) || {};
const opt: MetricConfigOpt = nodeMetricConfig[index] || {}; const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
const v = aggregation(metric.value, opt); return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${opt.unit || ""}</div>`;
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${v} ${opt.unit || ""}</div>`;
}); });
const tipHtml = [ const tipHtml = [
`<div class="mb-5"><span class="grey">name: </span>${data.name}</div><div class="mb-5"><span class="grey">layer: </span>${data.layer}</div>`, `<div class="mb-5"><span class="grey">name: </span>${data.name}</div><div class="mb-5"><span class="grey">layer: </span>${data.layer}</div>`,

View File

@ -144,7 +144,7 @@ limitations under the License. -->
import { useSelectorStore } from "@/store/modules/selectors"; import { useSelectorStore } from "@/store/modules/selectors";
import { useTopologyStore } from "@/store/modules/topology"; import { useTopologyStore } from "@/store/modules/topology";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { EntityType, DepthList, MetricModes, CallTypes } from "@/views/dashboard/data"; import { EntityType, DepthList, CallTypes } from "@/views/dashboard/data";
import router from "@/router"; import router from "@/router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import Settings from "../config/Settings.vue"; import Settings from "../config/Settings.vue";
@ -154,9 +154,7 @@ limitations under the License. -->
import { useAppStoreWithOut } from "@/store/modules/app"; import { useAppStoreWithOut } from "@/store/modules/app";
import getDashboard from "@/hooks/useDashboardsSession"; import getDashboard from "@/hooks/useDashboardsSession";
import type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
import { aggregation } from "@/hooks/useMetricsProcessor";
import icons from "@/assets/img/icons"; import icons from "@/assets/img/icons";
import { useQueryTopologyMetrics } from "@/hooks/useMetricsProcessor";
import { layout, computeLevels, changeNode } from "../components/utils/layout"; import { layout, computeLevels, changeNode } from "../components/utils/layout";
import zoom from "@/views/dashboard/related/components/utils/zoom"; import zoom from "@/views/dashboard/related/components/utils/zoom";
import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor"; import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor";
@ -231,15 +229,9 @@ limitations under the License. -->
} }
async function update() { async function update() {
if (settings.value.metricMode === MetricModes.Expression) { topologyStore.queryNodeExpressions(settings.value.nodeExpressions || []);
topologyStore.queryNodeExpressions(settings.value.nodeExpressions || []); topologyStore.getLinkExpressions(settings.value.linkClientExpressions || [], CallTypes.Client);
topologyStore.getLinkExpressions(settings.value.linkClientExpressions || [], CallTypes.Client); topologyStore.getLinkExpressions(settings.value.linkServerExpressions || [], CallTypes.Server);
topologyStore.getLinkExpressions(settings.value.linkServerExpressions || [], CallTypes.Server);
} else {
topologyStore.queryNodeMetrics(settings.value.nodeMetrics || []);
topologyStore.getLinkClientMetrics(settings.value.linkClientMetrics || []);
topologyStore.getLinkServerMetrics(settings.value.linkServerMetrics || []);
}
window.addEventListener("resize", resize); window.addEventListener("resize", resize);
await initLegendMetrics(); await initLegendMetrics();
@ -288,67 +280,33 @@ limitations under the License. -->
if (!topologyStore.nodes.length) { if (!topologyStore.nodes.length) {
return; return;
} }
if (settings.value.metricMode === MetricModes.Expression) {
const expression = props.config.legendMQE && props.config.legendMQE.expression; const expression = props.config.legendMQE && props.config.legendMQE.expression;
if (!expression) { if (!expression) {
return; return;
} }
const { getExpressionQuery } = useQueryTopologyExpressionsProcessor([expression], topologyStore.nodes); const { getExpressionQuery } = useQueryTopologyExpressionsProcessor([expression], topologyStore.nodes);
const param = getExpressionQuery(); const param = getExpressionQuery();
const res = await topologyStore.getNodeExpressionValue(param); const res = await topologyStore.getNodeExpressionValue(param);
if (res.errors) { if (res.errors) {
ElMessage.error(res.errors); ElMessage.error(res.errors);
} else {
topologyStore.setLegendValues([expression], res.data);
}
} else { } else {
const names = props.config.legend.map((d: any) => d.name); topologyStore.setLegendValues([expression], res.data);
if (!names.length) {
return;
}
const ids = topologyStore.nodes.map((d: Node) => d.id);
if (ids.length) {
const param = await useQueryTopologyMetrics(names, ids);
const res = await topologyStore.getLegendMetrics(param);
if (res.errors) {
ElMessage.error(res.errors);
}
}
} }
} }
function getNodeStatus(d: any) { function getNodeStatus(d: any) {
const { legend, legendMQE } = settings.value; const { legendMQE } = settings.value;
if (settings.value.metricMode === MetricModes.Expression) { if (!legendMQE) {
if (!legendMQE) {
return icons.CUBE;
}
if (!legendMQE.expression) {
return icons.CUBE;
}
return Number(d[legendMQE.expression]) && d.isReal ? icons.CUBEERROR : icons.CUBE;
}
if (!legend) {
return icons.CUBE; return icons.CUBE;
} }
if (!legend.length) { if (!legendMQE.expression) {
return icons.CUBE; return icons.CUBE;
} }
let c = true; return Number(d[legendMQE.expression]) && d.isReal ? icons.CUBEERROR : icons.CUBE;
for (const l of legend) {
if (l.condition === "<") {
c = c && d[l.name] < Number(l.value);
} else {
c = c && d[l.name] > Number(l.value);
}
}
return c && d.isReal ? icons.CUBEERROR : icons.CUBE;
} }
function showNodeTip(event: MouseEvent, data: Node) { function showNodeTip(event: MouseEvent, data: Node) {
const nodeMetrics: string[] = const nodeMetrics: string[] = settings.value.nodeExpressions || [];
(settings.value.metricMode === MetricModes.Expression
? settings.value.nodeExpressions
: settings.value.nodeMetrics) || [];
const nodeMetricConfig = settings.value.nodeMetricConfig || []; const nodeMetricConfig = settings.value.nodeMetricConfig || [];
const html = nodeMetrics.map((m, index) => { const html = nodeMetrics.map((m, index) => {
const metric = const metric =
@ -356,8 +314,9 @@ limitations under the License. -->
topologyStore.nodeMetricValue[m].values.find((val: { id: string; value: unknown }) => val.id === data.id)) || topologyStore.nodeMetricValue[m].values.find((val: { id: string; value: unknown }) => val.id === data.id)) ||
{}; {};
const opt: MetricConfigOpt = nodeMetricConfig[index] || {}; const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
const v = aggregation(metric.value, opt); return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${v} ${opt.unit || "unknown"}</div>`; opt.unit || "unknown"
}</div>`;
}); });
const tipHtml = [ const tipHtml = [
`<div class="mb-5"><span class="grey">name: </span>${ `<div class="mb-5"><span class="grey">name: </span>${
@ -373,24 +332,19 @@ limitations under the License. -->
.html(tipHtml); .html(tipHtml);
} }
function showLinkTip(event: MouseEvent, data: Call) { function showLinkTip(event: MouseEvent, data: Call) {
const linkClientMetrics: string[] = const linkClientMetrics: string[] = settings.value.linkClientExpressions || [];
settings.value.metricMode === MetricModes.Expression
? settings.value.linkClientExpressions
: settings.value.linkClientMetrics || [];
const linkServerMetricConfig: MetricConfigOpt[] = settings.value.linkServerMetricConfig || []; const linkServerMetricConfig: MetricConfigOpt[] = settings.value.linkServerMetricConfig || [];
const linkClientMetricConfig: MetricConfigOpt[] = settings.value.linkClientMetricConfig || []; const linkClientMetricConfig: MetricConfigOpt[] = settings.value.linkClientMetricConfig || [];
const linkServerMetrics: string[] = const linkServerMetrics: string[] = settings.value.linkServerExpressions || [];
settings.value.metricMode === MetricModes.Expression
? settings.value.linkServerExpressions
: settings.value.linkServerMetrics || [];
const htmlServer = linkServerMetrics.map((m, index) => { const htmlServer = linkServerMetrics.map((m, index) => {
const metric = topologyStore.linkServerMetrics[m].values.find( const metric = topologyStore.linkServerMetrics[m].values.find(
(val: { id: string; value: unknown }) => val.id === data.id, (val: { id: string; value: unknown }) => val.id === data.id,
); );
if (metric) { if (metric) {
const opt: MetricConfigOpt = linkServerMetricConfig[index] || {}; const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
const v = aggregation(metric.value, opt); return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${v} ${opt.unit || ""}</div>`; opt.unit || ""
}</div>`;
} }
}); });
const htmlClient = linkClientMetrics.map((m: string, index: number) => { const htmlClient = linkClientMetrics.map((m: string, index: number) => {
@ -399,8 +353,9 @@ limitations under the License. -->
(val: { id: string; value: unknown }) => val.id === data.id, (val: { id: string; value: unknown }) => val.id === data.id,
); );
if (metric) { if (metric) {
const v = aggregation(metric.value, opt); return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${v} ${opt.unit || ""}</div>`; opt.unit || ""
}</div>`;
} }
}); });
const html = [ const html = [