This is an automated email from the ASF dual-hosted git repository.
aloyszhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git
The following commit(s) were added to refs/heads/master by this push:
new f3dee08a52 [INLONG-10691][Dashboard] Add a metrics query page (#10692)
f3dee08a52 is described below
commit f3dee08a52a2f6ef6b11791b58d4f8f83956c673
Author: kamianlaida <[email protected]>
AuthorDate: Tue Jul 23 12:27:34 2024 +0800
[INLONG-10691][Dashboard] Add a metrics query page (#10692)
---
inlong-dashboard/src/ui/locales/cn.json | 3 +
inlong-dashboard/src/ui/locales/en.json | 3 +
.../src/ui/pages/GroupDetail/Audit/config.tsx | 2 +-
.../src/ui/pages/GroupDetail/Delay/config.tsx | 2 +-
.../Audit => ModuleAudit/AuditModule}/config.tsx | 64 +++++----
.../src/ui/pages/ModuleAudit/AuditModule/index.tsx | 155 +++++++++++++++++++++
.../src/ui/pages/ModuleAudit/index.tsx | 7 +-
.../pages/SynchronizeDetail/SyncAudit/config.tsx | 2 +-
8 files changed, 208 insertions(+), 30 deletions(-)
diff --git a/inlong-dashboard/src/ui/locales/cn.json
b/inlong-dashboard/src/ui/locales/cn.json
index 565f5fd6ac..88f10feb5d 100644
--- a/inlong-dashboard/src/ui/locales/cn.json
+++ b/inlong-dashboard/src/ui/locales/cn.json
@@ -559,6 +559,7 @@
"components.Layout.UserManual": "用户手册",
"components.Layout.Feedback": "建议反馈",
"components.HighSelect.Customize": "自定义",
+ "components.HighRadio.Customize": "自定义(ascii码)",
"components.HighSelect.SearchPlaceholder": "请输入关键字搜索",
"components.NodeSelect.Create": "新建节点",
"components.FieldList.SinkFieldName": "目标字段",
@@ -656,6 +657,7 @@
"pages.GroupDetail.Audit.Send": "发送成功",
"pages.GroupDetail.Audit.TimeStaticsDim": "粒度",
"pages.GroupDetail.Audit.Item": "审计项",
+ "pages.GroupDetail.Metric.Item": "指标项",
"pages.GroupDetail.Audit.Min": "分钟",
"pages.GroupDetail.Audit.Hour": "小时",
"pages.GroupDetail.Audit.Day": "天",
@@ -890,6 +892,7 @@
"pages.Tenant.config.CreateTime": "创建时间",
"pages.ModuleAudit.Ip": "IP 查询",
"pages.ModuleAudit.Id": "ID 查询",
+ "pages.ModuleAudit.Metric": "指标查询",
"pages.ModuleAudit.config.Ip": "机器 IP",
"pages.ModuleAudit.config.BenchmarkIndicator": "基准指标",
"pages.ModuleAudit.config.ComparativeIndicators": "对比指标",
diff --git a/inlong-dashboard/src/ui/locales/en.json
b/inlong-dashboard/src/ui/locales/en.json
index 809d676b54..8998a2d13f 100644
--- a/inlong-dashboard/src/ui/locales/en.json
+++ b/inlong-dashboard/src/ui/locales/en.json
@@ -559,6 +559,7 @@
"components.Layout.UserManual": "User manual",
"components.Layout.Feedback": "Feedback",
"components.HighSelect.Customize": "Customize",
+ "components.HighRadio.Customize": "Customize (ascii code)",
"components.HighSelect.SearchPlaceholder": "Please enter keyword...",
"components.NodeSelect.Create": "Create node",
"components.FieldList.SinkFieldName": "Sink field name",
@@ -660,6 +661,7 @@
"pages.GroupDetail.Audit.Day": "Day",
"pages.GroupDetail.Audit.Sink": "Sink",
"pages.GroupDetail.Audit.Item": "Audit item",
+ "pages.GroupDetail.Metric.Item": "Metric item",
"pages.GroupDetail.Audit.Total": "Total",
"pages.GroupDetail.Audit.DatepickerRule": "Out of selectable time range",
"pages.GroupDetail.Delay.QueryDate": "Query date",
@@ -890,6 +892,7 @@
"pages.Tenant.config.CreateTime": "Create time",
"pages.ModuleAudit.Ip": "Query by ip",
"pages.ModuleAudit.Id": "Query by id",
+ "pages.ModuleAudit.Metric": "Query by Metric",
"pages.ModuleAudit.config.Ip": "Machine ip",
"pages.ModuleAudit.config.BenchmarkIndicator": "Benchmark indicator",
"pages.ModuleAudit.config.ComparativeIndicators": "Comparative indicator",
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
index b61d119020..10cc589d50 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
@@ -160,7 +160,7 @@ let endTimeVisible = true;
export const getFormContent = (inlongGroupId, initialValues, onSearch,
onDataStreamSuccess) => [
{
type: 'select',
- label: i18n.t('pages.GroupDetail.Audit.DataStream'),
+ label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
name: 'inlongStreamId',
props: {
dropdownMatchSelectWidth: false,
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx
b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx
index 3cee25ee8c..4ebe19d8f5 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx
@@ -99,7 +99,7 @@ export const toTableData = (source, sourceDataMap) => {
export const getFormContent = (inlongGroupId, initialValues, onSearch,
onDataStreamSuccess) => [
{
type: 'select',
- label: i18n.t('pages.GroupDetail.Audit.DataStream'),
+ label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
name: 'inlongStreamId',
props: {
dropdownMatchSelectWidth: false,
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
similarity index 87%
copy from inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
copy to inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
index b61d119020..81d05bd15d 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/config.tsx
@@ -69,10 +69,11 @@ function getAuditLabel(auditId: number, nodeType?: string) {
}
export const toChartData = (source, sourceDataMap) => {
- const xAxisData = Object.keys(sourceDataMap);
+ console.log(source, sourceDataMap);
+ const xAxisData = Object.keys(sourceDataMap ? sourceDataMap : '12345');
return {
legend: {
- data: source.map(item => item.auditName),
+ data: source.map(item => item?.auditName),
},
tooltip: {
trigger: 'axis',
@@ -85,9 +86,9 @@ export const toChartData = (source, sourceDataMap) => {
type: 'value',
},
series: source.map(item => ({
- name: item.auditName,
+ name: item?.auditName ? item?.auditName : '1233',
type: 'line',
- data: xAxisData.map(logTs => sourceDataMap[logTs]?.[item.auditId] || 0),
+ data: xAxisData.map(logTs => sourceDataMap[logTs]?.[item?.auditId] || 0),
})),
};
};
@@ -157,69 +158,73 @@ export const getSourceDataWithCommas = sourceData => {
let endTimeVisible = true;
-export const getFormContent = (inlongGroupId, initialValues, onSearch,
onDataStreamSuccess) => [
+export const getFormContent = (initialValues, onSearch, onDataStreamSuccess)
=> [
{
type: 'select',
- label: i18n.t('pages.GroupDetail.Audit.DataStream'),
- name: 'inlongStreamId',
- props: {
+ label: i18n.t('pages.ModuleAudit.config.InlongGroupId'),
+ name: 'inlongGroupId',
+ props: values => ({
dropdownMatchSelectWidth: false,
showSearch: true,
+ allowClear: true,
options: {
requestAuto: true,
requestTrigger: ['onOpen', 'onSearch'],
requestService: keyword => ({
- url: '/stream/list',
+ url: '/group/list',
method: 'POST',
data: {
keyword,
pageNum: 1,
pageSize: 100,
- inlongGroupId,
+ inlongGroupMode: 0,
},
}),
requestParams: {
formatResult: result =>
result?.list.map(item => ({
- label: item.inlongStreamId,
- value: item.inlongStreamId,
+ label: item.inlongGroupId,
+ value: item.inlongGroupId,
})) || [],
- onSuccess: onDataStreamSuccess,
},
},
- },
+ }),
rules: [{ required: true }],
},
{
type: 'select',
- label: i18n.t('pages.GroupDetail.Audit.Sink'),
- name: 'sinkId',
+ label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
+ name: 'inlongStreamId',
props: values => ({
dropdownMatchSelectWidth: false,
showSearch: true,
+ allowClear: true,
+ disabled: !Boolean(values.inlongGroupId),
options: {
+ requestAuto: true,
requestTrigger: ['onOpen', 'onSearch'],
requestService: keyword => ({
- url: '/sink/list',
+ url: '/stream/list',
method: 'POST',
data: {
keyword,
pageNum: 1,
pageSize: 100,
- inlongGroupId,
- inlongStreamId: values.inlongStreamId,
+ inlongGroupId: values.inlongGroupId,
},
}),
requestParams: {
formatResult: result =>
result?.list.map(item => ({
- label: item.sinkName + ` ( ${sinks.find(c => c.value ===
item.sinkType)?.label} )`,
- value: item.id,
+ label: item.inlongStreamId,
+ value: item.inlongStreamId,
})) || [],
},
},
}),
+ rules: [{ required: true }],
},
+
{
type: 'datepicker',
label: i18n.t('pages.GroupDetail.Audit.StartDate'),
@@ -287,8 +292,9 @@ export const getFormContent = (inlongGroupId,
initialValues, onSearch, onDataStr
},
{
type: 'select',
- label: i18n.t('pages.GroupDetail.Audit.Item'),
+ label: i18n.t('pages.GroupDetail.Metric.Item'),
name: 'auditIds',
+ rules: [{ required: true }],
props: {
style: {
width: 200,
@@ -301,7 +307,12 @@ export const getFormContent = (inlongGroupId,
initialValues, onSearch, onDataStr
requestAuto: true,
requestTrigger: ['onOpen'],
requestService: () => {
- return request('/audit/getAuditBases');
+ return request({
+ url: '/audit/getAuditBases',
+ params: {
+ isMetric: true,
+ },
+ });
},
requestParams: {
formatResult: result => {
@@ -339,12 +350,13 @@ export const getTableColumns = (source, dim) => {
title: item.auditName,
dataIndex: item.auditId,
render: text => {
+ let color = 'black';
if (text?.includes('+')) {
- return <span style={{ color: 'red' }}>{text}</span>;
+ color = 'red';
} else if (text?.includes('-')) {
- return <span style={{ color: 'green' }}>{text}</span>;
+ color = 'green';
}
- return <span>{text}</span>;
+ return <span style={{ color: color }}>{text}</span>;
},
}));
return [
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx
b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx
new file mode 100644
index 0000000000..5dbed68dd7
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/AuditModule/index.tsx
@@ -0,0 +1,155 @@
+/*
+ * 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 React, { useMemo, useState } from 'react';
+import FormGenerator, { useForm } from '@/ui/components/FormGenerator';
+import HighTable from '@/ui/components/HighTable';
+import { useRequest } from '@/ui/hooks';
+import { timestampFormat } from '@/core/utils';
+import Charts from '@/ui/components/Charts';
+import {
+ getFormContent,
+ toChartData,
+ toTableData,
+ getTableColumns,
+ timeStaticsDimList,
+} from './config';
+import { Table } from 'antd';
+import i18n from '@/i18n';
+
+export const auditModule = 'audit';
+const Comp: React.FC = () => {
+ const [form] = useForm();
+
+ const [query, setQuery] = useState({
+ inlongGroupId: '',
+ inlongStreamId: '',
+ startDate: +new Date(),
+ endDate: +new Date(),
+ timeStaticsDim: timeStaticsDimList[0].value,
+ });
+
+ const { data: sourceData = [], run } = useRequest(
+ {
+ url: '/audit/list',
+ method: 'POST',
+ data: {
+ ...query,
+ startDate: timestampFormat(query.startDate, 'yyyy-MM-dd'),
+ endDate: timestampFormat(query.endDate, 'yyyy-MM-dd'),
+ },
+ },
+ {
+ ready: Boolean(query.inlongStreamId),
+ formatResult: result => result.sort((a, b) => (a.auditId - b.auditId > 0
? 1 : -1)),
+ },
+ );
+
+ const sourceDataMap = useMemo(() => {
+ const flatArr = sourceData
+ .reduce(
+ (acc, cur) =>
+ acc.concat(
+ cur.auditSet.map(item => ({
+ ...item,
+ auditId: cur.auditId,
+ })),
+ ),
+ [],
+ )
+ .sort((a, b) => {
+ const aT = +new Date(query.timeStaticsDim === 'HOUR' ? `${a.logTs}:00`
: a.logTs);
+ const bT = +new Date(query.timeStaticsDim === 'HOUR' ? `${b.logTs}:00`
: b.logTs);
+ return aT - bT;
+ });
+ const output = flatArr.reduce((acc, cur) => {
+ if (!acc[cur.logTs]) {
+ acc[cur.logTs] = {};
+ }
+ acc[cur.logTs] = {
+ ...acc[cur.logTs],
+ [cur.auditId]: cur.count,
+ };
+ return acc;
+ }, {});
+ return output;
+ }, [sourceData, query.timeStaticsDim]);
+
+ const onSearch = async () => {
+ let values = await form.validateFields();
+ if (values.timeStaticsDim == 'MINUTE') {
+ setQuery(prev => ({ ...prev, endDate: prev.startDate }));
+ }
+ run();
+ };
+
+ const onDataStreamSuccess = data => {
+ const defaultDataStream = data[0]?.value;
+ if (defaultDataStream) {
+ form.setFieldsValue({ inlongStreamId: defaultDataStream });
+ setQuery(prev => ({ ...prev, inlongStreamId: defaultDataStream }));
+ run();
+ }
+ };
+
+ return (
+ <>
+ <div style={{ marginBottom: 40 }}>
+ <FormGenerator
+ form={form}
+ layout="inline"
+ content={getFormContent(query, onSearch, onDataStreamSuccess)}
+ style={{ marginBottom: 30 }}
+ onFilter={allValues =>
+ setQuery({
+ ...allValues,
+ startDate: +allValues.startDate.$d,
+ endDate: +allValues.endDate.$d,
+ })
+ }
+ />
+ <Charts height={400} option={toChartData(sourceData, sourceDataMap)}
forceUpdate={true} />
+ </div>
+
+ <HighTable
+ table={{
+ columns: getTableColumns(sourceData, query.timeStaticsDim),
+ dataSource: toTableData(sourceData, sourceDataMap),
+ rowKey: 'logTs',
+ summary: () => (
+ <Table.Summary fixed>
+ <Table.Summary.Row>
+ <Table.Summary.Cell index={0}>
+ {i18n.t('pages.GroupDetail.Audit.Total')}
+ </Table.Summary.Cell>
+ {sourceData.map((row, index) => (
+ <Table.Summary.Cell key={index} index={index + 1}>
+ {row.auditSet.reduce((total, item) => total + item.count,
0).toLocaleString()}
+ </Table.Summary.Cell>
+ ))}
+ </Table.Summary.Row>
+ </Table.Summary>
+ ),
+ }}
+ />
+ </>
+ );
+};
+
+export default Comp;
diff --git a/inlong-dashboard/src/ui/pages/ModuleAudit/index.tsx
b/inlong-dashboard/src/ui/pages/ModuleAudit/index.tsx
index 52385292b7..f0fc7b826e 100644
--- a/inlong-dashboard/src/ui/pages/ModuleAudit/index.tsx
+++ b/inlong-dashboard/src/ui/pages/ModuleAudit/index.tsx
@@ -24,7 +24,7 @@ import { useHistory, useParams } from '@/ui/hooks';
import i18n from '@/i18n';
import IpModule, { ipModule as ipModuleName } from './IpModule';
import IdModule, { idModule as idModuleName } from './IdModule';
-
+import AuditModule, { auditModule as auditModuleName } from
'@/ui/pages/ModuleAudit/AuditModule';
const tabList = [
{
tab: i18n.t('pages.ModuleAudit.Id'),
@@ -36,6 +36,11 @@ const tabList = [
key: ipModuleName,
content: <IpModule />,
},
+ {
+ tab: i18n.t('pages.ModuleAudit.Metric'),
+ key: auditModuleName,
+ content: <AuditModule />,
+ },
];
const tabListMap = tabList.reduce(
diff --git
a/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncAudit/config.tsx
b/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncAudit/config.tsx
index b6c6ded33a..e5b29f7f4e 100644
--- a/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncAudit/config.tsx
+++ b/inlong-dashboard/src/ui/pages/SynchronizeDetail/SyncAudit/config.tsx
@@ -95,7 +95,7 @@ export const toTableData = (source, sourceDataMap) => {
export const getFormContent = (inlongGroupId, initialValues, onSearch,
onDataStreamSuccess) => [
{
type: 'select',
- label: i18n.t('pages.GroupDetail.Audit.DataStream'),
+ label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
name: 'inlongStreamId',
props: {
dropdownMatchSelectWidth: false,