|
- /*
- * @Author: 赵伟
- * @Date: 2024-05-16 08:47:46
- * @Description: 日志组件
- */
-
- import { ExperimentStatus } from '@/enums';
- import { useStateRef } from '@/hooks/useStateRef';
- import { getExperimentPodsLog } from '@/services/experiment/index.js';
- import { DoubleRightOutlined, DownOutlined, UpOutlined } from '@ant-design/icons';
- import { Button } from 'antd';
- import classNames from 'classnames';
- import dayjs from 'dayjs';
- import { useEffect, useRef, useState } from 'react';
- import { ExperimentLog } from '../LogList';
- import styles from './index.less';
-
- export type LogGroupProps = ExperimentLog & {
- status?: ExperimentStatus; // 实验状态
- };
-
- type Log = {
- start_time: string; // 日志开始时间
- log_content: string; // 日志内容
- pod_name: string; // pod名称
- };
-
- function LogGroup({
- log_type = 'normal',
- pod_name = '',
- log_content = '',
- start_time,
- status,
- }: LogGroupProps) {
- const [collapse, setCollapse] = useState(true);
- const [logList, setLogList] = useState<Log[]>([]);
- const [completed, setCompleted] = useState(false);
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [_isMouseDown, setIsMouseDown, isMouseDownRef] = useStateRef(false);
- const socketRef = useRef<WebSocket | undefined>(undefined);
- const retryRef = useRef(2); // 等待 2 秒,重试 3 次
- const logElementRef = useRef<HTMLDivElement | null>(null);
- // 如果是【运行中】状态,设置 hasRun 为 true,【运行中】或者从【运行中】切换到别的状态时,不显示【更多】按钮
- const [hasRun, setHasRun] = useState(false);
- if (status === ExperimentStatus.Running && !hasRun) {
- setHasRun(true);
- }
-
- // 进入页面时,滚动到底部
- useEffect(() => {
- scrollToBottom(false);
- }, []);
-
- useEffect(() => {
- // 建立 socket 连接
- const setupSockect = () => {
- let { host } = location;
- if (process.env.NODE_ENV === 'development') {
- host = '172.20.32.235:31213';
- }
- const socket = new WebSocket(
- `ws://${host}/newlog/realtimeLog?start=${start_time}&query={pod="${pod_name}"}`,
- );
-
- socket.addEventListener('open', () => {
- console.log('WebSocket is open now.');
- });
-
- socket.addEventListener('close', (event) => {
- console.log('WebSocket is closed:', event);
- // 有时候会出现连接失败,重试 3 次
- if (event.code !== 1000 && retryRef.current > 0) {
- retryRef.current -= 1;
- setTimeout(() => {
- setupSockect();
- }, 2 * 1000);
- }
- });
-
- socket.addEventListener('error', (event) => {
- console.error('WebSocket error observed:', event);
- });
-
- socket.addEventListener('message', (event) => {
- // console.log('message received.', event);
- if (!event.data) {
- return;
- }
- try {
- const data = JSON.parse(event.data);
- const streams = data.streams;
- if (!streams || !Array.isArray(streams)) {
- return;
- }
- let startTime = start_time;
- const logContent = streams.reduce((result, item) => {
- const values = item.values;
- return (
- result +
- values.reduce((prev: string, cur: [string, string]) => {
- const [time, value] = cur;
- startTime = time;
- const str = `[${dayjs(Number(time) / 1.0e6).format(
- 'YYYY-MM-DD HH:mm:ss',
- )}] ${value}`;
- return prev + str;
- }, '')
- );
- }, '');
- const logDetail: Log = {
- start_time: startTime!,
- log_content: logContent,
- pod_name: pod_name,
- };
- setLogList((oldList) => oldList.concat(logDetail));
- if (!isMouseDownRef.current && logContent) {
- setTimeout(() => {
- scrollToBottom();
- }, 100);
- }
- } catch (error) {
- console.error('JSON parse error: ', error);
- }
- });
-
- socketRef.current = socket;
- };
-
- // 关闭 socket
- const closeSocket = () => {
- if (socketRef.current) {
- socketRef.current.close(1000, 'completed');
- socketRef.current = undefined;
- }
- };
-
- if (status === ExperimentStatus.Running) {
- setupSockect();
- }
-
- return () => {
- closeSocket();
- };
- }, [status, start_time, pod_name, isMouseDownRef]);
-
- // 鼠标拖到中不滚动到底部
- useEffect(() => {
- const mouseDown = () => {
- setIsMouseDown(true);
- };
- const mouseUp = () => {
- setIsMouseDown(false);
- };
- document.addEventListener('mousedown', mouseDown);
- document.addEventListener('mouseup', mouseUp);
- return () => {
- document.removeEventListener('mousedown', mouseDown);
- document.removeEventListener('mouseup', mouseUp);
- };
- }, [setIsMouseDown]);
-
- // 请求日志
- const requestExperimentPodsLog = async () => {
- const last = logList[logList.length - 1];
- const startTime = last ? last.start_time : start_time;
- const params = {
- pod_name,
- start_time: startTime,
- };
- const res = await getExperimentPodsLog(params);
- const { log_detail } = res.data || {};
- if (log_detail) {
- setLogList((oldList) => oldList.concat(log_detail));
-
- if (!isMouseDownRef.current && log_detail.log_content) {
- setTimeout(() => {
- scrollToBottom();
- }, 100);
- }
- }
-
- // 判断是否日志是否加载完成
- if (!log_detail?.log_content) {
- setCompleted(true);
- }
- };
-
- // 处理折叠
- const handleCollapse = async () => {
- if (!collapse) {
- setCollapse(true);
- return;
- }
-
- if (logList.length === 0) {
- try {
- await requestExperimentPodsLog();
- setCollapse(false);
- } catch (error) {
- return Promise.reject(error);
- }
- } else {
- setCollapse(false);
- }
- };
-
- // 加载更多
- const loadMore = () => {
- requestExperimentPodsLog();
- };
-
- // 滚动到底部
- const scrollToBottom = (smooth: boolean = true) => {
- // const element = document.getElementById(listId);
- // if (element) {
- // const optons: ScrollToOptions = {
- // top: element.scrollHeight,
- // behavior: smooth ? 'smooth' : 'instant',
- // };
- // element.scrollTo(optons);
- // }
- logElementRef?.current?.scrollIntoView({
- block: 'end',
- behavior: smooth ? 'smooth' : 'instant',
- });
- };
-
- const showLog = (log_type === 'resource' && !collapse) || log_type === 'normal';
- const logText = log_content + logList.map((v) => v.log_content).join('');
- const showMoreBtn = !hasRun && !completed && showLog && logText !== '';
- return (
- <div className={styles['log-group']} ref={logElementRef}>
- {log_type === 'resource' && (
- <div className={styles['log-group__pod']} onClick={handleCollapse}>
- <div className={styles['log-group__pod__name']}>{pod_name}</div>
- {collapse ? <DownOutlined /> : <UpOutlined />}
- </div>
- )}
- {showLog && (
- <div
- className={classNames(styles['log-group__detail'], {
- [styles['log-group__detail--empty']]: !logText,
- })}
- >
- {logText ? logText : '暂无日志'}
- </div>
- )}
- <div className={styles['log-group__more-button']}>
- {showMoreBtn && (
- <Button type="text" style={{ color: 'white' }} onClick={loadMore}>
- 更多
- <DoubleRightOutlined rotate={90} />
- </Button>
- )}
- </div>
- </div>
- );
- }
-
- export default LogGroup;
|