fix: large logs page stuck

This commit is contained in:
jialin
2024-11-05 19:46:47 +08:00
parent 5f2115fff6
commit f684a3b0ae
8 changed files with 114 additions and 40 deletions
+1
View File
@@ -12,3 +12,4 @@
/.mfsu /.mfsu
.swc .swc
.DS_Store .DS_Store
.idea
+10
View File
@@ -19,6 +19,16 @@ export default defineConfig({
history: { history: {
type: 'hash' type: 'hash'
}, },
analyze: {
analyzerMode: 'server',
analyzerPort: 8888,
openAnalyzer: true,
// generate stats file while ANALYZE_DUMP exist
generateStatsFile: false,
statsFilename: 'stats.json',
logLevel: 'info',
defaultSizes: 'parsed' // stat // gzip
},
base: process.env.npm_config_base || '/', base: process.env.npm_config_base || '/',
...(isProduction ...(isProduction
? { ? {
+3 -8
View File
@@ -26,10 +26,9 @@ const LogsInner: React.FC<LogsInnerProps> = (props) => {
index: data.length - 1, index: data.length - 1,
align: 'bottom' align: 'bottom'
}); });
console.log('scrollToBottom', stopScroll.current, data.length);
} }
}, 200), }, 200),
[scroller.current, stopScroll.current, data] [data, scroller.current, stopScroll.current]
); );
const debounceResetStopScroll = _.debounce(() => { const debounceResetStopScroll = _.debounce(() => {
@@ -66,7 +65,6 @@ const LogsInner: React.FC<LogsInnerProps> = (props) => {
(e: any) => { (e: any) => {
const isBottom = isScrollBottom(logsWrapper.current); const isBottom = isScrollBottom(logsWrapper.current);
const isTop = isScrollTop(logsWrapper.current); const isTop = isScrollTop(logsWrapper.current);
console.log('isBottom===', isBottom);
if (isBottom) { if (isBottom) {
stopScroll.current = false; stopScroll.current = false;
} else { } else {
@@ -79,10 +77,7 @@ const LogsInner: React.FC<LogsInnerProps> = (props) => {
); );
useEffect(() => { useEffect(() => {
if (!stopScroll.current && data.length > 0) { updataPositionToBottom();
updataPositionToBottom();
console.log('updataPositionToBottom', stopScroll.current);
}
}, [updataPositionToBottom]); }, [updataPositionToBottom]);
useEffect(() => { useEffect(() => {
@@ -115,7 +110,7 @@ const LogsInner: React.FC<LogsInnerProps> = (props) => {
} }
}} }}
> >
{(item: any) => ( {(item: any, index: number) => (
<div key={item.uid} className="text"> <div key={item.uid} className="text">
{item.content} {item.content}
</div> </div>
+1 -1
View File
@@ -97,7 +97,6 @@ const useParseAnsi = () => {
break; break;
case 'J': // clear the screen case 'J': // clear the screen
if (n === 2) { if (n === 2) {
console.log('clear====');
screen = [['']]; screen = [['']];
cursorRow = 0; cursorRow = 0;
cursorCol = 0; cursorCol = 0;
@@ -132,6 +131,7 @@ const useParseAnsi = () => {
uid: setId() uid: setId()
}); });
} }
result.push({ result.push({
content: output, content: output,
uid: setId() uid: setId()
+38 -4
View File
@@ -5,6 +5,44 @@
position: absolute; position: absolute;
top: 16px; top: 16px;
right: 16px; right: 16px;
width: 40px;
height: 130px;
.pg-inner {
position: relative;
&::before {
content: '';
position: absolute;
top: 30px;
bottom: 30px;
right: 0;
width: 100px;
height: 60px;
&:hover {
.pagination {
display: flex;
}
}
}
.pagination {
display: none;
}
&:hover {
.pagination {
display: flex;
}
}
&.at-top {
.pagination {
display: flex;
}
}
}
} }
.loading { .loading {
@@ -18,10 +56,6 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
background-color: rgba(255, 255, 255, 25%); background-color: rgba(255, 255, 255, 25%);
.ant-spin-dot-holder {
color: rgba(255, 255, 255, 90%);
}
} }
.copy { .copy {
+50 -25
View File
@@ -1,9 +1,16 @@
import useSetChunkFetch from '@/hooks/use-chunk-fetch'; import useSetChunkFetch from '@/hooks/use-chunk-fetch';
import useSetChunkRequest from '@/hooks/use-chunk-request';
import { Spin } from 'antd'; import { Spin } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import _ from 'lodash'; import _ from 'lodash';
import { memo, useCallback, useEffect, useRef, useState } from 'react'; import {
forwardRef,
memo,
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState
} from 'react';
import { controlSeqRegex, replaceLineRegex } from './config'; import { controlSeqRegex, replaceLineRegex } from './config';
import LogsInner from './logs-inner'; import LogsInner from './logs-inner';
import LogsPagination from './logs-pagination'; import LogsPagination from './logs-pagination';
@@ -15,28 +22,38 @@ interface LogsViewerProps {
content?: string; content?: string;
url: string; url: string;
params?: object; params?: object;
ref?: any;
diffHeight?: number; diffHeight?: number;
} }
const LogsViewer: React.FC<LogsViewerProps> = (props) => { const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
const { diffHeight, url } = props; const { diffHeight, url } = props;
const { pageSize, page, setPage, setTotalPage, totalPage } = const { pageSize, page, setPage, setTotalPage, totalPage } =
useLogsPagination(); useLogsPagination();
const { setChunkRequest } = useSetChunkRequest();
const { setChunkFetch } = useSetChunkFetch(); const { setChunkFetch } = useSetChunkFetch();
const chunkRequedtRef = useRef<any>(null); const chunkRequedtRef = useRef<any>(null);
const cacheDataRef = useRef<any>(''); const cacheDataRef = useRef<any>('');
const [logs, setLogs] = useState<any[]>([]); const [logs, setLogs] = useState<any[]>([]);
const logParseWorker = useRef<any>(null); const logParseWorker = useRef<any>(null);
const tail = useRef<any>(pageSize); const tail = useRef<any>(pageSize);
const isLoadend = useRef<any>(false); const [isLoadend, setIsLoadend] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isAtTop, setIsAtTop] = useState(false); const [isAtTop, setIsAtTop] = useState(false);
useImperativeHandle(ref, () => ({
abort() {
chunkRequedtRef.current?.current?.abort?.();
}
}));
useEffect(() => { useEffect(() => {
logParseWorker.current?.terminate?.(); logParseWorker.current?.terminate?.();
logParseWorker.current = new Worker( logParseWorker.current = new Worker(
new URL('./parse-worker.ts', import.meta.url) // @ts-ignore
new URL('./parse-worker.ts', import.meta.url),
{
type: 'module'
}
); );
logParseWorker.current.onmessage = (event: any) => { logParseWorker.current.onmessage = (event: any) => {
@@ -53,7 +70,7 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
const debounceLoading = _.debounce(() => { const debounceLoading = _.debounce(() => {
setLoading(false); setLoading(false);
}, 100); }, 200);
const isClean = useCallback((input: string) => { const isClean = useCallback((input: string) => {
let match = controlSeqRegex.exec(input) || []; let match = controlSeqRegex.exec(input) || [];
@@ -64,9 +81,9 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
const getLastPage = useCallback( const getLastPage = useCallback(
(data: string) => { (data: string) => {
const list = _.split(data, '\n'); const list = _.split(data.trim(), '\n');
isLoadend.current = list.length < pageSize; console.log('list.length', list.length);
console.log('isLoadend.current===', isLoadend.current, list.length);
if (list.length <= pageSize) { if (list.length <= pageSize) {
setTotalPage(1); setTotalPage(1);
return data; return data;
@@ -84,7 +101,7 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
); );
const getPrePage = useCallback(() => { const getPrePage = useCallback(() => {
const list = _.split(cacheDataRef.current, '\n'); const list = _.split(cacheDataRef.current.trim(), '\n');
let newPage = page - 1; let newPage = page - 1;
if (newPage < 1) { if (newPage < 1) {
newPage = 1; newPage = 1;
@@ -99,7 +116,7 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
}, [page, pageSize]); }, [page, pageSize]);
const getNextPage = useCallback(() => { const getNextPage = useCallback(() => {
const list = _.split(cacheDataRef.current, '\n'); const list = _.split(cacheDataRef.current.trim(), '\n');
let newPage = page + 1; let newPage = page + 1;
if (newPage > totalPage) { if (newPage > totalPage) {
newPage = totalPage; newPage = totalPage;
@@ -146,17 +163,18 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
}; };
const handleOnScroll = useCallback( const handleOnScroll = useCallback(
(isTop: boolean) => { async (isTop: boolean) => {
setIsAtTop(isTop && isLoadend.current); setIsAtTop(isTop);
if (loading || isLoadend.current) { if (loading || isLoadend || logs.length < pageSize) {
return; return;
} }
if (isTop && !isLoadend.current) { if (isTop && !isLoadend) {
tail.current = undefined; tail.current = undefined;
createChunkConnection(); createChunkConnection();
setIsLoadend(true);
} }
}, },
[loading, isLoadend.current] [loading, isLoadend, logs.length, pageSize]
); );
useEffect(() => { useEffect(() => {
@@ -182,19 +200,26 @@ const LogsViewer: React.FC<LogsViewerProps> = (props) => {
loading: loading && isAtTop loading: loading && isAtTop
})} })}
></Spin> ></Spin>
{totalPage > 1 && isAtTop && ( {totalPage > 1 && (
<div className="pg"> <div className="pg">
<LogsPagination <div
page={page} className={classNames('pg-inner', {
total={totalPage} 'at-top': isAtTop
onNext={getNextPage} })}
onPrev={getPrePage} >
></LogsPagination> <LogsPagination
page={page}
total={totalPage}
pageSize={pageSize}
onNext={getNextPage}
onPrev={getPrePage}
></LogsPagination>
</div>
</div> </div>
)} )}
</div> </div>
</div> </div>
); );
}; });
export default memo(LogsViewer); export default memo(LogsViewer);
+3 -1
View File
@@ -8,6 +8,7 @@ interface RequestConfig {
beforeReconnect?: () => void; beforeReconnect?: () => void;
params?: object; params?: object;
byLine?: boolean; byLine?: boolean;
watch?: boolean;
contentType?: 'json' | 'text'; contentType?: 'json' | 'text';
} }
@@ -70,6 +71,7 @@ const useSetChunkFetch = () => {
const fetchChunkRequest = async ({ const fetchChunkRequest = async ({
url, url,
handler, handler,
watch,
byLine = false, byLine = false,
params = {} params = {}
}: RequestConfig) => { }: RequestConfig) => {
@@ -79,7 +81,7 @@ const useSetChunkFetch = () => {
const response = await fetch( const response = await fetch(
`v1${url}?${qs.stringify({ `v1${url}?${qs.stringify({
...params, ...params,
watch: true watch: watch === undefined ? true : watch
})}`, })}`,
{ {
method: 'GET', method: 'GET',
@@ -17,6 +17,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
height: 420 height: 420
}); });
const isFullScreenRef = React.useRef(false); const isFullScreenRef = React.useRef(false);
const logsViewerRef = React.useRef<any>(null);
const intl = useIntl(); const intl = useIntl();
const viewportHeight = window.innerHeight; const viewportHeight = window.innerHeight;
const viewHeight = viewportHeight - 86; const viewHeight = viewportHeight - 86;
@@ -31,6 +32,11 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
}); });
}, []); }, []);
const handleCancel = useCallback(() => {
logsViewerRef.current?.abort();
onCancel();
}, [onCancel]);
useEffect(() => { useEffect(() => {
if (open) { if (open) {
isFullScreenRef.current = false; isFullScreenRef.current = false;
@@ -53,7 +59,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
} }
open={open} open={open}
centered={true} centered={true}
onCancel={onCancel} onCancel={handleCancel}
destroyOnClose={true} destroyOnClose={true}
closeIcon={true} closeIcon={true}
maskClosable={false} maskClosable={false}
@@ -67,6 +73,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
footer={null} footer={null}
> >
<LogsViewer <LogsViewer
ref={logsViewerRef}
height={modalSize.height} height={modalSize.height}
diffHeight={93} diffHeight={93}
url={url} url={url}