This is an automated email from the ASF dual-hosted git repository. vernedeng pushed a commit to branch branch-1.8 in repository https://gitbox.apache.org/repos/asf/inlong.git
commit fa05e0290a962c92aa82b1c66bbcaa9fa25fc24e Author: Lizhen <88174078+bluew...@users.noreply.github.com> AuthorDate: Tue Jul 18 17:39:27 2023 +0800 [INLONG-8548][Dashboard] Data access supports displaying transmission delay (#8555) Co-authored-by: Charles Zhang <dockerzh...@apache.org> Co-authored-by: healchow <healc...@gmail.com> (cherry picked from commit b2a25b57a96db02421d3c1a639cff7dd3b60a785) --- inlong-dashboard/src/ui/locales/cn.json | 4 + inlong-dashboard/src/ui/locales/en.json | 4 + .../src/ui/pages/GroupDetail/Audit/index.tsx | 2 - .../src/ui/pages/GroupDetail/Delay/config.tsx | 211 +++++++++++++++++++++ .../pages/GroupDetail/{Audit => Delay}/index.tsx | 64 ++++++- .../src/ui/pages/GroupDetail/index.tsx | 7 + 6 files changed, 287 insertions(+), 5 deletions(-) diff --git a/inlong-dashboard/src/ui/locales/cn.json b/inlong-dashboard/src/ui/locales/cn.json index e2d567bbae..67d8004dc8 100644 --- a/inlong-dashboard/src/ui/locales/cn.json +++ b/inlong-dashboard/src/ui/locales/cn.json @@ -617,6 +617,7 @@ "pages.GroupDetail.Sinks": "数据目标", "pages.GroupDetail.Audit": "审计对账", "pages.GroupDetail.Resource": "资源详情", + "pages.GroupDetail.Delay": "传输时延", "pages.GroupDetail.Resource.Info": " 信息", "pages.GroupDetail.Audit.DataStream": "数据流", "pages.GroupDetail.Audit.StartDate": "开始日期", @@ -631,6 +632,9 @@ "pages.GroupDetail.Audit.Hour": "小时", "pages.GroupDetail.Audit.Day": "天", "pages.GroupDetail.Audit.Sink": "数据目标", + "pages.GroupDetail.Delay.QueryDate": "查询日期", + "pages.GroupDetail.Delay.AverageTitle": "平均传输时延 (ms)", + "pages.GroupDetail.Delay.RealTimeTitle": "传输时延 (ms)", "pages.GroupDetail.Stream.Preview": "数据预览", "pages.GroupDetail.Stream.Dt": "数据时间 (dt)", "pages.GroupDetail.Stream.Content": "数据内容", diff --git a/inlong-dashboard/src/ui/locales/en.json b/inlong-dashboard/src/ui/locales/en.json index 4542a6bd57..60e49aa8df 100644 --- a/inlong-dashboard/src/ui/locales/en.json +++ b/inlong-dashboard/src/ui/locales/en.json @@ -616,6 +616,7 @@ "pages.GroupDetail.Sources": "Sources", "pages.GroupDetail.Sinks": "Sinks", "pages.GroupDetail.Audit": "Audit", + "pages.GroupDetail.Delay": "Transmission Delay", "pages.GroupDetail.Resource": "Resource Detail", "pages.GroupDetail.Resource.Info": " Info", "pages.GroupDetail.Audit.DataStream": "DataStream", @@ -631,6 +632,9 @@ "pages.GroupDetail.Audit.Hour": "Hour", "pages.GroupDetail.Audit.Day": "Day", "pages.GroupDetail.Audit.Sink": "Sink", + "pages.GroupDetail.Delay.QueryDate": "Query Date", + "pages.GroupDetail.Delay.AverageTitle": "Average Transmission Delay (ms)", + "pages.GroupDetail.Delay.RealTimeTitle": "Transmission Delay (ms)", "pages.GroupDetail.Stream.Preview": "Data Preview", "pages.GroupDetail.Stream.Dt": "Data Time(dt)", "pages.GroupDetail.Stream.Content": "Data Content", diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx index 93d1a26a51..3b368c03ad 100644 --- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx +++ b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx @@ -81,12 +81,10 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => { const output = flatArr.reduce((acc, cur) => { if (!acc[cur.logTs]) { acc[cur.logTs] = {}; - acc[cur.delay] = ''; } acc[cur.logTs] = { ...acc[cur.logTs], [cur.auditId]: cur.count, - [cur.delay]: cur.delay, }; return acc; }, {}); diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx new file mode 100644 index 0000000000..67554a1372 --- /dev/null +++ b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/config.tsx @@ -0,0 +1,211 @@ +/* + * 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 from 'react'; +import { Button } from 'antd'; +import dayjs from 'dayjs'; +import i18n from '@/i18n'; +import { sinks } from '@/plugins/sinks'; + +export const timeStaticsDimList = [ + { + label: i18n.t('pages.GroupDetail.Audit.Min'), + value: 'MINUTE', + }, + { + label: i18n.t('pages.GroupDetail.Audit.Hour'), + value: 'HOUR', + }, +]; + +const auditList = ['Agent', 'DataProxy', 'Sort'].reduce((acc, item, index) => { + return acc.concat([ + { + label: `${item} ${i18n.t('pages.GroupDetail.Audit.Receive')}`, + value: index * 2 + 3, + }, + { + label: `${item} ${i18n.t('pages.GroupDetail.Audit.Send')}`, + value: index * 2 + 4, + }, + ]); +}, []); + +const auditMap = auditList.reduce( + (acc, cur) => ({ + ...acc, + [cur.value]: cur, + }), + {}, +); + +function getAuditLabel(auditId: number, nodeType?: string) { + const id = +auditId; + const item = id >= 9 ? auditMap[id % 2 ? 7 : 8] : auditMap[id]; + const label = item?.label || id; + const sinkLabel = sinks.find(c => c.value === nodeType)?.label; + return nodeType ? `${label}(${sinkLabel})` : label; +} + +export const toChartData = (source, sourceDataMap) => { + const xAxisData = Object.keys(sourceDataMap); + return { + legend: { + data: source.map(item => getAuditLabel(item.auditId, item.nodeType)), + }, + tooltip: { + trigger: 'axis', + }, + xAxis: { + type: 'category', + data: xAxisData, + }, + yAxis: { + type: 'value', + }, + series: source.map(item => ({ + name: getAuditLabel(item.auditId, item.nodeType), + type: 'line', + data: xAxisData.map(logTs => sourceDataMap[logTs]?.[item.auditId] || 0), + })), + }; +}; + +export const toTableData = (source, sourceDataMap) => { + return Object.keys(sourceDataMap) + .reverse() + .map(logTs => ({ + ...sourceDataMap[logTs], + logTs, + })); +}; + +export const getFormContent = (inlongGroupId, initialValues, onSearch, onDataStreamSuccess) => [ + { + type: 'select', + label: i18n.t('pages.GroupDetail.Audit.DataStream'), + name: 'inlongStreamId', + props: { + dropdownMatchSelectWidth: false, + options: { + requestAuto: true, + requestService: { + url: '/stream/list', + method: 'POST', + data: { + pageNum: 1, + pageSize: 1000, + inlongGroupId, + }, + }, + requestParams: { + formatResult: result => + result?.list.map(item => ({ + label: item.inlongStreamId, + value: item.inlongStreamId, + })) || [], + onSuccess: onDataStreamSuccess, + }, + }, + }, + rules: [{ required: true }], + }, + { + type: 'select', + label: i18n.t('pages.GroupDetail.Audit.Sink'), + name: 'sinkId', + props: values => ({ + dropdownMatchSelectWidth: false, + options: { + requestService: { + url: '/sink/list', + method: 'POST', + data: { + pageNum: 1, + pageSize: 1000, + inlongGroupId, + inlongStreamId: values.inlongStreamId, + }, + }, + requestParams: { + formatResult: result => + result?.list.map(item => ({ + label: item.sinkName + ` ( ${sinks.find(c => c.value === item.sinkType)?.label} )`, + value: item.id, + })) || [], + }, + }, + }), + }, + { + type: 'datepicker', + label: i18n.t('pages.GroupDetail.Delay.QueryDate'), + name: 'startDate', + initialValue: dayjs(initialValues.startDate), + props: { + allowClear: false, + format: 'YYYY-MM-DD', + }, + }, + { + type: 'select', + label: i18n.t('pages.GroupDetail.Audit.TimeStaticsDim'), + name: 'timeStaticsDim', + initialValue: initialValues.timeStaticsDim, + props: { + dropdownMatchSelectWidth: false, + options: timeStaticsDimList, + }, + }, + { + type: ( + <Button type="primary" onClick={onSearch}> + {i18n.t('pages.GroupDetail.Audit.Search')} + </Button> + ), + }, +]; + +export const getTableColumns = source => { + const data = source.map(item => ({ + title: getAuditLabel(item.auditId, item.nodeType), + dataIndex: item.auditId, + render: text => text || 0, + })); + return [ + { + title: i18n.t('pages.GroupDetail.Audit.Time'), + dataIndex: 'logTs', + }, + ].concat(data); +}; + +export const getDelayColumns = source => { + const data = source.map(item => ({ + title: getAuditLabel(item.auditId, item.nodeType), + dataIndex: item.auditId, + render: text => text || 0, + })); + return [ + { + title: i18n.t('pages.GroupDetail.Audit.Time'), + dataIndex: 'logTs', + }, + ].concat(data); +}; diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/index.tsx similarity index 67% copy from inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx copy to inlong-dashboard/src/ui/pages/GroupDetail/Delay/index.tsx index 93d1a26a51..1aa6d96237 100644 --- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx +++ b/inlong-dashboard/src/ui/pages/GroupDetail/Delay/index.tsx @@ -31,6 +31,8 @@ import { getTableColumns, timeStaticsDimList, } from './config'; +import { Divider, Table } from 'antd'; +import i18n from '@/i18n'; type Props = CommonInterface; @@ -61,6 +63,24 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => { }, ); + const { data: dayData = [], run: getDayData } = useRequest( + { + url: '/audit/list', + method: 'POST', + data: { + ...query, + timeStaticsDim: 'DAY', + startDate: timestampFormat(query.startDate, 'yyyy-MM-dd'), + endDate: timestampFormat(query.startDate, 'yyyy-MM-dd'), + inlongGroupId, + }, + }, + { + ready: Boolean(query.inlongStreamId), + formatResult: result => result.sort((a, b) => (a.auditId - b.auditId > 0 ? 1 : -1)), + }, + ); + const sourceDataMap = useMemo(() => { const flatArr = sourceData .reduce( @@ -81,21 +101,50 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => { const output = flatArr.reduce((acc, cur) => { if (!acc[cur.logTs]) { acc[cur.logTs] = {}; - acc[cur.delay] = ''; } acc[cur.logTs] = { ...acc[cur.logTs], - [cur.auditId]: cur.count, - [cur.delay]: cur.delay, + [cur.auditId]: cur.count === 0 ? cur.delay : Math.floor(cur.delay / cur.count), }; return acc; }, {}); return output; }, [sourceData, query.timeStaticsDim]); + const dayDataMap = useMemo(() => { + const flatArr = dayData + .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 === 0 ? cur.delay : Math.floor(cur.delay / cur.count), + }; + return acc; + }, {}); + return output; + }, [dayData, query.timeStaticsDim]); + const onSearch = async () => { await form.validateFields(); run(); + getDayData(); }; const onDataStreamSuccess = data => { @@ -104,6 +153,7 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => { form.setFieldsValue({ inlongStreamId: defaultDataStream }); setQuery(prev => ({ ...prev, inlongStreamId: defaultDataStream })); run(); + getDayData(); } }; @@ -126,6 +176,14 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => { <Charts height={400} option={toChartData(sourceData, sourceDataMap)} /> </div> + <Divider>{i18n.t('pages.GroupDetail.Delay.AverageTitle')}</Divider> + <Table + columns={getTableColumns(dayData)} + dataSource={toTableData(dayData, dayDataMap)} + pagination={false} + /> + + <Divider style={{ marginTop: 50 }}>{i18n.t('pages.GroupDetail.Delay.RealTimeTitle')}</Divider> <HighTable table={{ columns: getTableColumns(sourceData), diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx b/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx index 98704a094d..f42fc8d433 100644 --- a/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx +++ b/inlong-dashboard/src/ui/pages/GroupDetail/index.tsx @@ -28,6 +28,7 @@ import Info from './Info'; import DataStream from './DataStream'; import Audit from './Audit'; import ResourceInfo from './ResourceInfo'; +import Delay from './Delay'; const Comp: React.FC = () => { const { t } = useTranslation(); @@ -83,6 +84,12 @@ const Comp: React.FC = () => { content: ResourceInfo, hidden: isReadonly || isCreate, }, + { + label: t('pages.GroupDetail.Delay'), + value: 'Delay', + content: Delay, + hidden: isReadonly || isCreate, + }, ].filter(item => !item.hidden), [isReadonly, isCreate, t], );