From ceae10cbfa9234b98021791d4daca9507113805b Mon Sep 17 00:00:00 2001 From: innerpeacez Date: Thu, 13 Apr 2023 17:08:00 +0800 Subject: [PATCH 01/29] feat: add elasticsearch menu (#257) --- src/locales/lang/en.ts | 1 + src/locales/lang/es.ts | 1 + src/locales/lang/zh.ts | 1 + src/router/data/database.ts | 16 ++++++++++++++++ 4 files changed, 19 insertions(+) diff --git a/src/locales/lang/en.ts b/src/locales/lang/en.ts index 119cbe34..675da923 100644 --- a/src/locales/lang/en.ts +++ b/src/locales/lang/en.ts @@ -385,5 +385,6 @@ const msg = { AWSGateway: "AWS API Gateway", APIGateway: "API Gateway", redis: "Redis", + elasticsearch: "Elasticsearch", }; export default msg; diff --git a/src/locales/lang/es.ts b/src/locales/lang/es.ts index 2e69f8fd..6cde3210 100644 --- a/src/locales/lang/es.ts +++ b/src/locales/lang/es.ts @@ -384,5 +384,6 @@ const msg = { AWSGateway: "AWS API Gateway", APIGateway: "API Gateway", redis: "Redis", + elasticsearch: "Elasticsearch", }; export default msg; diff --git a/src/locales/lang/zh.ts b/src/locales/lang/zh.ts index 1dfd5aff..6adcedb8 100644 --- a/src/locales/lang/zh.ts +++ b/src/locales/lang/zh.ts @@ -382,5 +382,6 @@ const msg = { AWSGateway: "AWS API Gateway", APIGateway: "API Gateway", redis: "Redis", + elasticsearch: "Elasticsearch", }; export default msg; diff --git a/src/router/data/database.ts b/src/router/data/database.ts index 57a9d8b4..f95c500c 100644 --- a/src/router/data/database.ts +++ b/src/router/data/database.ts @@ -90,6 +90,22 @@ export default [ layer: "REDIS", }, }, + { + path: "/elasticsearch", + name: "Elasticsearch", + meta: { + title: "elasticsearch", + layer: "ELASTICSEARCH", + }, + }, + { + path: "/elasticsearch/tab/:activeTabIndex", + name: "ElasticsearchActiveTabIndex", + meta: { + notShow: true, + layer: "ELASTICSEARCH", + }, + }, ], }, ]; From 3f178f89f824f996ccd1850167d8647937328db4 Mon Sep 17 00:00:00 2001 From: Fine0830 Date: Thu, 13 Apr 2023 21:29:23 +0800 Subject: [PATCH 02/29] feat: support isEmptyValue flag for metrics query (#256) --- src/hooks/data.ts | 11 ++++++--- src/hooks/useMetricsProcessor.ts | 39 +++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/hooks/data.ts b/src/hooks/data.ts index 0484aca0..195483f9 100644 --- a/src/hooks/data.ts +++ b/src/hooks/data.ts @@ -22,6 +22,7 @@ export enum MetricQueryTypes { READHEATMAP = "readHeatMap", ReadSampledRecords = "readSampledRecords", ReadRecords = "readRecords", + ReadNullableMetricsValue = "readNullableMetricsValue", } export enum Calculations { @@ -70,10 +71,14 @@ export const RespFields: Indexable = { readMetricsValues: `{ label values { - values {value} + values {value isEmptyValue} } }`, - readMetricsValue: "", + readMetricsValue: ``, + readNullableMetricsValue: `{ + value + isEmptyValue + }`, sortMetrics: `{ name id @@ -83,7 +88,7 @@ export const RespFields: Indexable = { readLabeledMetricsValues: `{ label values { - values {value} + values {value isEmptyValue} } }`, readHeatMap: `{ diff --git a/src/hooks/useMetricsProcessor.ts b/src/hooks/useMetricsProcessor.ts index dbea34bf..3013f163 100644 --- a/src/hooks/useMetricsProcessor.ts +++ b/src/hooks/useMetricsProcessor.ts @@ -114,9 +114,10 @@ export function useQueryProcessor(config: Indexable) { } if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) { return `${name}${index}: ${metricType}(condition: $condition${index}, labels: $labels${index}, duration: $duration)${RespFields[metricType]}`; - } else { - return `${name}${index}: ${metricType}(condition: $condition${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}}`; @@ -156,7 +157,9 @@ export function useSourceProcessor( 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 }) => aggregation(Number(d.value), c)); + 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; @@ -166,7 +169,8 @@ export function useSourceProcessor( } } if (type === MetricQueryTypes.ReadMetricsValue) { - source[m] = aggregation(Number(Object.values(resp.data)[0]), c); + const v = Object.values(resp.data)[0] || {}; + source[m] = v.isEmptyValue ? NaN : aggregation(Number(v.value), c); } if ( ( @@ -250,7 +254,9 @@ export function useQueryPodsMetrics( const labels = (c.labelsIndex || "").split(",").map((item: string) => item.replace(/^\s*|\s*$/g, "")); conditions[`labels${index}${idx}`] = labels; } - return `${name}${index}${idx}: ${metricType}(condition: $condition${index}${idx}, ${labelStr}duration: $duration)${RespFields[metricType]}`; + const t = + metricType === MetricQueryTypes.ReadMetricsValue ? MetricQueryTypes.ReadNullableMetricsValue : metricType; + return `${name}${index}${idx}: ${t}(condition: $condition${index}${idx}, ${labelStr}duration: $duration)${RespFields[t]}`; }); return f; }); @@ -281,7 +287,8 @@ export function usePodsSource( const c: any = (config.metricConfig && config.metricConfig[index]) || {}; const key = name + idx + index; if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValue) { - d[name] = aggregation(resp.data[key], c); + const v = resp.data[key]; + d[name] = v.isEmptyValue ? NaN : aggregation(v.value, c); if (idx === 0) { names.push(name); metricConfigArr.push(c); @@ -293,7 +300,9 @@ export function usePodsSource( 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 }) => aggregation(val.value, 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); @@ -306,7 +315,9 @@ export function usePodsSource( const labelsIdx = (c.labelsIndex || "").split(",").map((item: string) => item.replace(/^\s*|\s*$/g, "")); for (let i = 0; i < resVal.length; i++) { const item = resVal[i]; - const values = item.values.values.map((d: { value: number }) => aggregation(Number(d.value), c)); + const 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) { @@ -356,8 +367,12 @@ export function useQueryTopologyMetrics(metrics: string[], ids: string[]) { return { queryStr, conditions }; } -function calculateExp(arr: { value: number }[], config: { calculation?: string }): (number | string)[] { - const sum = arr.map((d: { value: number }) => d.value).reduce((a, b) => a + b); +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 }) => d.value).reduce((a, b) => a + b) : 0; let data: (number | string)[] = []; switch (config.calculation) { case Calculations.Average: @@ -370,7 +385,9 @@ function calculateExp(arr: { value: number }[], config: { calculation?: string } data = [(sum / arr.length / 10000).toFixed(2)]; break; default: - data = arr.map((d) => aggregation(d.value, config)); + data = list.map((d: { value: number; isEmptyValue: boolean }) => + d.isEmptyValue ? NaN : aggregation(d.value, config), + ); break; } return data; From ce585d6e08b513c2019f9760ffb3323a8c3ead50 Mon Sep 17 00:00:00 2001 From: Fine0830 Date: Fri, 14 Apr 2023 10:35:30 +0800 Subject: [PATCH 03/29] feat: set default value for `showSymbol` in the line graph (#258) --- .../dashboard/configuration/widget/graph-styles/Line.vue | 3 ++- src/views/dashboard/data.ts | 2 +- src/views/dashboard/graphs/Line.vue | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/views/dashboard/configuration/widget/graph-styles/Line.vue b/src/views/dashboard/configuration/widget/graph-styles/Line.vue index ff92b8ce..5f421eb3 100644 --- a/src/views/dashboard/configuration/widget/graph-styles/Line.vue +++ b/src/views/dashboard/configuration/widget/graph-styles/Line.vue @@ -50,12 +50,13 @@ limitations under the License. --> import { useI18n } from "vue-i18n"; import { useDashboardStore } from "@/store/modules/dashboard"; import Legend from "./components/Legend.vue"; + import { isDef } from "@/utils/is"; const { t } = useI18n(); const dashboardStore = useDashboardStore(); const graph = computed(() => dashboardStore.selectedGrid.graph || {}); const smooth = ref(graph.value.smooth); - const showSymbol = ref(graph.value.showSymbol); + const showSymbol = ref(isDef(graph.value.showSymbol) ? graph.value.showSymbol : true); const step = ref(graph.value.step); function updateConfig(param: { [key: string]: unknown }) { diff --git a/src/views/dashboard/data.ts b/src/views/dashboard/data.ts index 2ab85dce..3e68f50e 100644 --- a/src/views/dashboard/data.ts +++ b/src/views/dashboard/data.ts @@ -65,7 +65,7 @@ export const DefaultGraphConfig: { [key: string]: any } = { type: "Line", step: false, smooth: false, - showSymbol: false, + showSymbol: true, showXAxis: true, showYAxis: true, }, diff --git a/src/views/dashboard/graphs/Line.vue b/src/views/dashboard/graphs/Line.vue index 7df33dc7..b35e142a 100644 --- a/src/views/dashboard/graphs/Line.vue +++ b/src/views/dashboard/graphs/Line.vue @@ -30,6 +30,7 @@ limitations under the License. --> import type { LineConfig, EventParams, RelatedTrace, Filters } from "@/types/dashboard"; import Legend from "./components/Legend.vue"; import useLegendProcess from "@/hooks/useLegendProcessor"; + import { isDef } from "@/utils/is"; /*global defineProps, defineEmits */ const emits = defineEmits(["click"]); @@ -72,9 +73,8 @@ limitations under the License. --> data: props.data[i].map((item: any, itemIndex: number) => [props.intervalTime[itemIndex], item]), name: i, type: "line", - symbol: "circle", - symbolSize: 8, - showSymbol: props.config.showSymbol, + symbolSize: 5, + showSymbol: isDef(props.config.showSymbol) ? props.config.showSymbol : true, step: props.config.step, smooth: props.config.smooth, lineStyle: { From 7257858921839d43f9b6fd8900fa78eaa7bb98b4 Mon Sep 17 00:00:00 2001 From: Fine0830 Date: Fri, 14 Apr 2023 17:09:30 +0800 Subject: [PATCH 04/29] feat: update options for line graph (#259) --- src/views/dashboard/graphs/Line.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/dashboard/graphs/Line.vue b/src/views/dashboard/graphs/Line.vue index b35e142a..fdfdbb4c 100644 --- a/src/views/dashboard/graphs/Line.vue +++ b/src/views/dashboard/graphs/Line.vue @@ -73,12 +73,13 @@ limitations under the License. --> data: props.data[i].map((item: any, itemIndex: number) => [props.intervalTime[itemIndex], item]), name: i, type: "line", - symbolSize: 5, + symbol: "circle", + symbolSize: 4, showSymbol: isDef(props.config.showSymbol) ? props.config.showSymbol : true, step: props.config.step, smooth: props.config.smooth, lineStyle: { - width: 1.5, + width: 2, type: "solid", }, }; From 7dde4820de5b074cf9050cbbe06c6b3c1c497835 Mon Sep 17 00:00:00 2001 From: Fine0830 Date: Sun, 16 Apr 2023 10:53:09 +0800 Subject: [PATCH 05/29] build(deps): bump element-plus from 2.0.2 to 2.1.0 (#260) --- package-lock.json | 54 +++++++++++++-------- package.json | 2 +- src/views/dashboard/graphs/Card.vue | 11 ++++- src/views/dashboard/graphs/EndpointList.vue | 2 +- src/views/dashboard/graphs/InstanceList.vue | 2 +- src/views/dashboard/graphs/ServiceList.vue | 2 +- src/views/dashboard/graphs/style.scss | 4 -- src/views/dashboard/panel/Tool.vue | 4 +- 8 files changed, 48 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index ebf1e905..74f83dc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "d3-flame-graph": "^4.1.3", "d3-tip": "^0.9.1", "echarts": "^5.2.2", - "element-plus": "^2.0.2", + "element-plus": "^2.1.0", "lodash": "^4.17.21", "monaco-editor": "^0.34.1", "pinia": "^2.0.28", @@ -1282,9 +1282,9 @@ } }, "node_modules/@element-plus/icons-vue": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-0.2.7.tgz", - "integrity": "sha512-S8kDbfVaWkQvbUYQE1ui448tzaHfUvyESCep9J6uPRlViyQPXjdIfwLBhV6AmQSOfFS8rL+xehJGhvzPXLrSBg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-1.1.4.tgz", + "integrity": "sha512-Iz/nHqdp1sFPmdzRwHkEQQA3lKvoObk8azgABZ81QUOpW9s/lUyQVUSh0tNtEPZXQlKwlSh7SPgoVxzrE0uuVQ==", "peerDependencies": { "vue": "^3.2.0" } @@ -5781,19 +5781,20 @@ "dev": true }, "node_modules/element-plus": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.0.2.tgz", - "integrity": "sha512-URjC0HwwiqtlLxqTmHXQ31WXrdAq4ChWyyn52OcQs3PRsnMPfahGVq2AWnfzzlzlhVeI5lY3HQiuB1zDathS+g==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.1.0.tgz", + "integrity": "sha512-PQM3LMv5qKf0sS/k+PXuBcmKl1Eb3b7fxKPyFHZ6eodZcNykbEMcpLZefQkvmDr6calVnuQ7TUnTm7Tm9LJXvg==", "dependencies": { "@ctrl/tinycolor": "^3.4.0", - "@element-plus/icons-vue": "^0.2.6", + "@element-plus/icons-vue": "^1.0.1", "@popperjs/core": "^2.11.2", - "@vueuse/core": "^7.6.0", + "@vueuse/core": "^7.7.1", "async-validator": "^4.0.7", - "dayjs": "^1.10.7", + "dayjs": "^1.10.8", + "escape-html": "^1.0.3", "lodash": "^4.17.21", "lodash-es": "^4.17.21", - "lodash-unified": "^1.0.1", + "lodash-unified": "^1.0.2", "memoize-one": "^6.0.0", "normalize-wheel-es": "^1.1.1" }, @@ -6405,6 +6406,11 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -17076,9 +17082,9 @@ } }, "@element-plus/icons-vue": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-0.2.7.tgz", - "integrity": "sha512-S8kDbfVaWkQvbUYQE1ui448tzaHfUvyESCep9J6uPRlViyQPXjdIfwLBhV6AmQSOfFS8rL+xehJGhvzPXLrSBg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-1.1.4.tgz", + "integrity": "sha512-Iz/nHqdp1sFPmdzRwHkEQQA3lKvoObk8azgABZ81QUOpW9s/lUyQVUSh0tNtEPZXQlKwlSh7SPgoVxzrE0uuVQ==", "requires": {} }, "@esbuild/android-arm": { @@ -20425,19 +20431,20 @@ "dev": true }, "element-plus": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.0.2.tgz", - "integrity": "sha512-URjC0HwwiqtlLxqTmHXQ31WXrdAq4ChWyyn52OcQs3PRsnMPfahGVq2AWnfzzlzlhVeI5lY3HQiuB1zDathS+g==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.1.0.tgz", + "integrity": "sha512-PQM3LMv5qKf0sS/k+PXuBcmKl1Eb3b7fxKPyFHZ6eodZcNykbEMcpLZefQkvmDr6calVnuQ7TUnTm7Tm9LJXvg==", "requires": { "@ctrl/tinycolor": "^3.4.0", - "@element-plus/icons-vue": "^0.2.6", + "@element-plus/icons-vue": "^1.0.1", "@popperjs/core": "^2.11.2", - "@vueuse/core": "^7.6.0", + "@vueuse/core": "^7.7.1", "async-validator": "^4.0.7", - "dayjs": "^1.10.7", + "dayjs": "^1.10.8", + "escape-html": "^1.0.3", "lodash": "^4.17.21", "lodash-es": "^4.17.21", - "lodash-unified": "^1.0.1", + "lodash-unified": "^1.0.2", "memoize-one": "^6.0.0", "normalize-wheel-es": "^1.1.1" }, @@ -20784,6 +20791,11 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", diff --git a/package.json b/package.json index 87fb1477..dbe3719b 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "d3-flame-graph": "^4.1.3", "d3-tip": "^0.9.1", "echarts": "^5.2.2", - "element-plus": "^2.0.2", + "element-plus": "^2.1.0", "lodash": "^4.17.21", "monaco-editor": "^0.34.1", "pinia": "^2.0.28", diff --git a/src/views/dashboard/graphs/Card.vue b/src/views/dashboard/graphs/Card.vue index 56c63c2e..0cd212f0 100644 --- a/src/views/dashboard/graphs/Card.vue +++ b/src/views/dashboard/graphs/Card.vue @@ -27,7 +27,7 @@ limitations under the License. --> {{ decodeURIComponent(unit) }} -
{{ t("noData") }}
+
{{ t("noData") }}
diff --git a/src/views/dashboard/configuration/widget/metric/Standard.vue b/src/views/dashboard/configuration/widget/metric/Standard.vue index 1172f631..8ba76a6a 100644 --- a/src/views/dashboard/configuration/widget/metric/Standard.vue +++ b/src/views/dashboard/configuration/widget/metric/Standard.vue @@ -42,7 +42,13 @@ limitations under the License. --> " /> -
+
{{ t("labelsIndex") }} " />
-
+
{{ t("aggregation") }} import { ref, watch, computed } from "vue"; import type { PropType } from "vue"; import { useI18n } from "vue-i18n"; - import { SortOrder, CalculationOpts } from "../../../data"; + import { SortOrder, CalculationOpts, MetricModes } from "../../../data"; import { useDashboardStore } from "@/store/modules/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard"; - import { ListChartTypes, ProtocolTypes } from "../../../data"; + import { ListChartTypes, ProtocolTypes, ExpressionResultType } from "../../../data"; /*global defineEmits, defineProps */ const props = defineProps({ @@ -110,24 +116,32 @@ limitations under the License. --> const { t } = useI18n(); const emit = defineEmits(["update"]); const dashboardStore = useDashboardStore(); + const isExpression = ref(dashboardStore.selectedGrid.metricMode === MetricModes.Expression); const currentMetric = ref({ ...props.currentMetricConfig, topN: props.currentMetricConfig.topN || 10, }); - const metricTypes = dashboardStore.selectedGrid.metricTypes || []; - const metricType = computed(() => (dashboardStore.selectedGrid.metricTypes || [])[props.index]); + const metricTypes = computed( + () => (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) + [ + ProtocolTypes.ReadLabeledMetricsValues, + ProtocolTypes.ReadMetricsValues, + ExpressionResultType.TIME_SERIES_VALUES, + ].includes(metricType.value) ); }); const isTopn = computed(() => [ProtocolTypes.SortMetrics, ProtocolTypes.ReadSampledRecords, ProtocolTypes.ReadRecords].includes( - metricTypes[props.index], + metricTypes.value[props.index], ), ); + const isExec = computed(() => dashboardStore.selectedGrid.metricMode === MetricModes.General); function updateConfig(index: number, param: { [key: string]: string }) { const key = Object.keys(param)[0]; if (!key) { diff --git a/src/views/dashboard/controls/Widget.vue b/src/views/dashboard/controls/Widget.vue index d52ebc96..0f36affd 100644 --- a/src/views/dashboard/controls/Widget.vue +++ b/src/views/dashboard/controls/Widget.vue @@ -59,6 +59,11 @@ limitations under the License. --> filters: data.filters || {}, relatedTrace: data.relatedTrace || {}, associate: data.associate || [], + metricMode: data.metricMode, + expressions: data.expressions || [], + typesOfMQE: data.typesOfMQE || [], + subExpressions: data.subExpressions || [], + subTypesOfMQE: data.subTypesOfMQE || [], }" :needQuery="needQuery" @click="clickHandle" @@ -76,10 +81,12 @@ limitations under the License. --> import { useSelectorStore } from "@/store/modules/selectors"; import graphs from "../graphs"; import { useI18n } from "vue-i18n"; - import { useQueryProcessor, useSourceProcessor, useGetMetricEntity } from "@/hooks/useMetricsProcessor"; + import { useQueryProcessor, useSourceProcessor } from "@/hooks/useMetricsProcessor"; + import { useExpressionsQueryProcessor, useExpressionsSourceProcessor } from "@/hooks/useExpressionsProcessor"; import { EntityType, ListChartTypes } from "../data"; import type { EventParams } from "@/types/dashboard"; import getDashboard from "@/hooks/useDashboardsSession"; + import { MetricModes } from "../data"; const props = { data: { @@ -113,10 +120,14 @@ limitations under the License. --> } async function queryMetrics() { - const metricTypes: string[] = props.data.metricTypes || []; - const metrics = props.data.metrics || []; - const catalog = await useGetMetricEntity(metrics[0], metricTypes[0]); - const params = await useQueryProcessor({ ...props.data, catalog }); + const isExpression = props.data.metricMode === MetricModes.Expression; + const params = isExpression + ? await useExpressionsQueryProcessor({ + metrics: props.data.expressions, + metricTypes: props.data.typesOfMQE, + metricConfig: props.data.metricConfig, + }) + : await useQueryProcessor({ ...props.data }); if (!params) { state.source = {}; @@ -133,7 +144,12 @@ limitations under the License. --> metricTypes: props.data.metricTypes || [], metricConfig: props.data.metricConfig || [], }; - state.source = useSourceProcessor(json, d); + const e = { + metrics: props.data.expressions || [], + metricTypes: props.data.typesOfMQE || [], + metricConfig: props.data.metricConfig || [], + }; + state.source = isExpression ? await useExpressionsSourceProcessor(json, e) : await useSourceProcessor(json, d); } function removeWidget() { @@ -169,7 +185,7 @@ limitations under the License. --> dashboardStore.selectWidget(props.data); } watch( - () => [props.data.metricTypes, props.data.metrics], + () => [props.data.metricTypes, props.data.metrics, props.data.expressions], () => { if (!dashboardStore.selectedGrid) { return; diff --git a/src/views/dashboard/data.ts b/src/views/dashboard/data.ts index 3e68f50e..5abd837f 100644 --- a/src/views/dashboard/data.ts +++ b/src/views/dashboard/data.ts @@ -56,6 +56,15 @@ export enum ProtocolTypes { ReadMetricsValues = "readMetricsValues", ReadMetricsValue = "readMetricsValue", } + +export enum ExpressionResultType { + UNKNOWN = "UNKNOWN", + SINGLE_VALUE = "SINGLE_VALUE", + TIME_SERIES_VALUES = "TIME_SERIES_VALUES", + SORTED_LIST = "SORTED_LIST", + RECORD_LIST = "RECORD_LIST", +} + export const DefaultGraphConfig: { [key: string]: any } = { Bar: { type: "Bar", @@ -322,3 +331,8 @@ export const RefreshOptions = [ { label: "Last 8 hours", value: "8", step: "HOUR" }, { label: "Last 7 days", value: "7", step: "DAY" }, ]; + +export enum MetricModes { + Expression = "Expression", + General = "General", +} diff --git a/src/views/dashboard/graphs/Bar.vue b/src/views/dashboard/graphs/Bar.vue index 302c4c69..98c2bd02 100644 --- a/src/views/dashboard/graphs/Bar.vue +++ b/src/views/dashboard/graphs/Bar.vue @@ -23,6 +23,7 @@ limitations under the License. --> import type { PropType } from "vue"; import type { BarConfig, EventParams, RelatedTrace, Filters } from "@/types/dashboard"; import useLegendProcess from "@/hooks/useLegendProcessor"; + import Legend from "./components/Legend.vue"; /*global defineProps, defineEmits */ const emits = defineEmits(["click"]); diff --git a/src/views/dashboard/graphs/EndpointList.vue b/src/views/dashboard/graphs/EndpointList.vue index 7cc95035..6781886a 100644 --- a/src/views/dashboard/graphs/EndpointList.vue +++ b/src/views/dashboard/graphs/EndpointList.vue @@ -41,6 +41,7 @@ limitations under the License. --> metrics: colMetrics, metricConfig, metricTypes, + metricMode, }" v-if="colMetrics.length" /> @@ -58,7 +59,8 @@ limitations under the License. --> import type { Endpoint } from "@/types/selector"; import { useDashboardStore } from "@/store/modules/dashboard"; import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useMetricsProcessor"; - import { EntityType } from "../data"; + import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor"; + import { EntityType, MetricModes } from "../data"; import router from "@/router"; import getDashboard from "@/hooks/useDashboardsSession"; import type { MetricConfigOpt } from "@/types/dashboard"; @@ -75,6 +77,11 @@ limitations under the License. --> i: string; metrics: string[]; metricTypes: string[]; + metricMode: string; + expressions: string[]; + typesOfMQE: string[]; + subExpressions: string[]; + subTypesOfMQE: string[]; } & { metricConfig: MetricConfigOpt[] } >, default: () => ({ @@ -98,6 +105,7 @@ limitations under the License. --> const colMetrics = ref([]); const metricConfig = ref(props.config.metricConfig || []); const metricTypes = ref(props.config.metricTypes || []); + const metricMode = ref(props.config.metricMode); if (props.needQuery) { queryEndpoints(); @@ -116,8 +124,20 @@ limitations under the License. --> endpoints.value = resp.data.pods || []; queryEndpointMetrics(endpoints.value); } - async function queryEndpointMetrics(currentPods: Endpoint[]) { - if (!currentPods.length) { + async function queryEndpointMetrics(arr: Endpoint[]) { + if (!arr.length) { + return; + } + const currentPods = arr.map((d: Endpoint) => { + return { + id: d.id, + value: d.value, + label: d.label, + merge: d.merge, + }; + }); + if (props.config.metricMode === MetricModes.Expression) { + queryEndpointExpressions(currentPods); return; } const metrics = props.config.metrics || []; @@ -141,6 +161,32 @@ limitations under the License. --> return; } endpoints.value = currentPods; + colMetrics.value = []; + metricTypes.value = []; + metricConfig.value = []; + } + async function queryEndpointExpressions(currentPods: Endpoint[]) { + const expressions = props.config.expressions || []; + const typesOfMQE = props.config.typesOfMQE || []; + const subExpressions = props.config.subExpressions || []; + + if (expressions.length && expressions[0] && typesOfMQE.length && typesOfMQE[0]) { + const params = await useExpressionsQueryPodsMetrics( + currentPods, + { ...props.config, metricConfig: metricConfig.value || [], typesOfMQE, expressions, subExpressions }, + EntityType[2].value, + ); + endpoints.value = params.data; + colMetrics.value = params.names; + metricTypes.value = params.metricTypesArr; + metricConfig.value = params.metricConfigArr; + + return; + } + endpoints.value = currentPods; + colMetrics.value = []; + metricTypes.value = []; + metricConfig.value = []; } function clickEndpoint(scope: any) { const { dashboard } = getDashboard({ @@ -160,12 +206,19 @@ limitations under the License. --> await queryEndpoints(); } watch( - () => [...(props.config.metricTypes || []), ...(props.config.metrics || []), ...(props.config.metricConfig || [])], + () => [ + ...(props.config.metricTypes || []), + ...(props.config.metrics || []), + ...(props.config.metricConfig || []), + ...(props.config.expressions || []), + props.config.metricMode, + ], (data, old) => { if (JSON.stringify(data) === JSON.stringify(old)) { return; } metricConfig.value = props.config.metricConfig; + metricMode.value = props.config.metricMode; queryEndpointMetrics(endpoints.value); }, ); diff --git a/src/views/dashboard/graphs/InstanceList.vue b/src/views/dashboard/graphs/InstanceList.vue index 0be09631..5b754ac1 100644 --- a/src/views/dashboard/graphs/InstanceList.vue +++ b/src/views/dashboard/graphs/InstanceList.vue @@ -40,6 +40,7 @@ limitations under the License. --> metrics: colMetrics, metricConfig, metricTypes, + metricMode, }" v-if="colMetrics.length" /> @@ -87,7 +88,8 @@ limitations under the License. --> import type { InstanceListConfig } from "@/types/dashboard"; import type { Instance } from "@/types/selector"; import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useMetricsProcessor"; - import { EntityType } from "../data"; + import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor"; + import { EntityType, MetricModes } from "../data"; import router from "@/router"; import getDashboard from "@/hooks/useDashboardsSession"; import type { MetricConfigOpt } from "@/types/dashboard"; @@ -102,6 +104,11 @@ limitations under the License. --> metrics: string[]; metricTypes: string[]; isEdit: boolean; + metricMode: string; + expressions: string[]; + typesOfMQE: string[]; + subExpressions: string[]; + subTypesOfMQE: string[]; } & { metricConfig: MetricConfigOpt[] } >, default: () => ({ @@ -126,6 +133,7 @@ limitations under the License. --> const metricConfig = ref(props.config.metricConfig || []); const metricTypes = ref(props.config.metricTypes || []); const pods = ref([]); // all instances + const metricMode = ref(props.config.metricMode); if (props.needQuery) { queryInstance(); } @@ -146,8 +154,23 @@ limitations under the License. --> queryInstanceMetrics(instances.value); } - async function queryInstanceMetrics(currentInstances: Instance[]) { - if (!currentInstances.length) { + async function queryInstanceMetrics(arr: Instance[]) { + if (!arr.length) { + return; + } + const currentInstances = arr.map((d: Instance) => { + return { + id: d.id, + value: d.value, + label: d.label, + merge: d.merge, + language: d.language, + instanceUUID: d.instanceUUID, + attributes: d.attributes, + }; + }); + if (props.config.metricMode === MetricModes.Expression) { + queryInstanceExpressions(currentInstances); return; } const metrics = props.config.metrics || []; @@ -172,6 +195,33 @@ limitations under the License. --> return; } instances.value = currentInstances; + colMetrics.value = []; + metricTypes.value = []; + metricConfig.value = []; + } + + async function queryInstanceExpressions(currentInstances: Instance[]) { + const expressions = props.config.expressions || []; + const typesOfMQE = props.config.typesOfMQE || []; + const subExpressions = props.config.subExpressions || []; + + if (expressions.length && expressions[0] && typesOfMQE.length && typesOfMQE[0]) { + const params = await useExpressionsQueryPodsMetrics( + currentInstances, + { ...props.config, metricConfig: metricConfig.value || [], typesOfMQE, expressions, subExpressions }, + EntityType[3].value, + ); + instances.value = params.data; + colMetrics.value = params.names; + metricTypes.value = params.metricTypesArr; + metricConfig.value = params.metricConfigArr; + + return; + } + instances.value = currentInstances; + colMetrics.value = []; + metricTypes.value = []; + metricConfig.value = []; } function clickInstance(scope: any) { @@ -207,12 +257,19 @@ limitations under the License. --> } watch( - () => [...(props.config.metricTypes || []), ...(props.config.metrics || []), ...(props.config.metricConfig || [])], + () => [ + ...(props.config.metricTypes || []), + ...(props.config.metrics || []), + ...(props.config.metricConfig || []), + ...(props.config.expressions || []), + props.config.metricMode, + ], (data, old) => { if (JSON.stringify(data) === JSON.stringify(old)) { return; } metricConfig.value = props.config.metricConfig; + metricMode.value = props.config.metricMode; queryInstanceMetrics(instances.value); }, ); diff --git a/src/views/dashboard/graphs/ServiceList.vue b/src/views/dashboard/graphs/ServiceList.vue index 44b1a512..31a7e1fa 100644 --- a/src/views/dashboard/graphs/ServiceList.vue +++ b/src/views/dashboard/graphs/ServiceList.vue @@ -52,6 +52,7 @@ limitations under the License. --> metrics: colMetrics, metricConfig, metricTypes, + metricMode, }" v-if="colMetrics.length" /> @@ -80,7 +81,8 @@ limitations under the License. --> import { useAppStoreWithOut } from "@/store/modules/app"; import type { Service } from "@/types/selector"; import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useMetricsProcessor"; - import { EntityType } from "../data"; + import { useExpressionsQueryPodsMetrics } from "@/hooks/useExpressionsProcessor"; + import { EntityType, MetricModes } from "../data"; import router from "@/router"; import getDashboard from "@/hooks/useDashboardsSession"; import type { MetricConfigOpt } from "@/types/dashboard"; @@ -100,6 +102,11 @@ limitations under the License. --> isEdit: boolean; names: string[]; metricConfig: MetricConfigOpt[]; + metricMode: string; + expressions: string[]; + typesOfMQE: string[]; + subExpressions: string[]; + subTypesOfMQE: string[]; } >, default: () => ({ dashboardName: "", fontSize: 12 }), @@ -119,6 +126,7 @@ limitations under the License. --> const sortServices = ref<(Service & { merge: boolean })[]>([]); const metricConfig = ref(props.config.metricConfig || []); const metricTypes = ref(props.config.metricTypes || []); + const metricMode = ref(props.config.metricMode); queryServices(); @@ -187,8 +195,23 @@ limitations under the License. --> router.push(path); } - async function queryServiceMetrics(currentServices: Service[]) { - if (!currentServices.length) { + async function queryServiceMetrics(arr: Service[]) { + if (!arr.length) { + return; + } + const currentServices = arr.map((d: Service) => { + return { + id: d.id, + value: d.value, + label: d.label, + layers: d.layers, + group: d.group, + normal: d.normal, + merge: d.merge, + }; + }); + if (props.config.metricMode === MetricModes.Expression) { + queryServiceExpressions(currentServices); return; } const metrics = props.config.metrics || []; @@ -211,6 +234,7 @@ limitations under the License. --> ...props.config, metricConfig: metricConfig.value || [], }); + services.value = data; colMetrics.value = names; metricTypes.value = metricTypesArr; @@ -219,6 +243,32 @@ limitations under the License. --> return; } services.value = currentServices; + colMetrics.value = []; + colMetrics.value = []; + metricTypes.value = []; + metricConfig.value = []; + } + async function queryServiceExpressions(currentServices: Service[]) { + const expressions = props.config.expressions || []; + const typesOfMQE = props.config.typesOfMQE || []; + const subExpressions = props.config.subExpressions || []; + + if (expressions.length && expressions[0] && typesOfMQE.length && typesOfMQE[0]) { + const params = await useExpressionsQueryPodsMetrics( + currentServices, + { ...props.config, metricConfig: metricConfig.value || [], typesOfMQE, expressions, subExpressions }, + EntityType[0].value, + ); + services.value = params.data; + colMetrics.value = params.names; + metricTypes.value = params.metricTypesArr; + metricConfig.value = params.metricConfigArr; + return; + } + services.value = currentServices; + colMetrics.value = []; + metricTypes.value = []; + metricConfig.value = []; } function objectSpanMethod(param: any): any { if (!props.config.showGroup) { @@ -251,15 +301,24 @@ limitations under the License. --> } watch( - () => [...(props.config.metricTypes || []), ...(props.config.metrics || []), ...(props.config.metricConfig || [])], + () => [ + ...(props.config.metricTypes || []), + ...(props.config.metrics || []), + ...(props.config.metricConfig || []), + ...(props.config.expressions || []), + ...(props.config.subExpressions || []), + props.config.metricMode, + ], (data, old) => { if (JSON.stringify(data) === JSON.stringify(old)) { return; } metricConfig.value = props.config.metricConfig; + metricMode.value = props.config.metricMode; queryServiceMetrics(services.value); }, ); + watch( () => appStore.durationTime, () => { diff --git a/src/views/dashboard/graphs/TopList.vue b/src/views/dashboard/graphs/TopList.vue index 1da016ed..ed4fbe9b 100644 --- a/src/views/dashboard/graphs/TopList.vue +++ b/src/views/dashboard/graphs/TopList.vue @@ -20,7 +20,7 @@ limitations under the License. -->
{{ i.value }} - {{ i.name }} + {{ i.name || i.id }}
diff --git a/src/views/dashboard/graphs/components/ColumnGraph.vue b/src/views/dashboard/graphs/components/ColumnGraph.vue index 06a4bef3..43299417 100644 --- a/src/views/dashboard/graphs/components/ColumnGraph.vue +++ b/src/views/dashboard/graphs/components/ColumnGraph.vue @@ -23,7 +23,7 @@ limitations under the License. --> diff --git a/src/views/dashboard/related/continuous-profiling/components/PolicyList.vue b/src/views/dashboard/related/continuous-profiling/components/PolicyList.vue index 314a1990..9787903c 100644 --- a/src/views/dashboard/related/continuous-profiling/components/PolicyList.vue +++ b/src/views/dashboard/related/continuous-profiling/components/PolicyList.vue @@ -37,6 +37,7 @@ limitations under the License. --> :class="{ selected: continousProfilingStore.selectedStrategy.id === i.id, }" + v-if="i.type" >
diff --git a/src/views/dashboard/related/task-timeline/Content.vue b/src/views/dashboard/related/task-timeline/Content.vue index c88fa898..b8cacf40 100644 --- a/src/views/dashboard/related/task-timeline/Content.vue +++ b/src/views/dashboard/related/task-timeline/Content.vue @@ -13,7 +13,7 @@ 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. -->