import React, { useState, useEffect } from 'react'; import { DatePicker, Table, Row, Col, Button, Select, Input, notification, message, Spin, } from 'antd'; import { SwapRightOutlined } from '@ant-design/icons'; import { Chart, Interval, Line, Tooltip, Axis } from 'bizcharts'; // import { DataSet } from '@antv/data-set'; import moment from 'moment'; import { post, PUBLISH_SERVICE } from '@/services/index'; import styles from './index.less'; const { Option } = Select; const { Search } = Input; const ServiceLog = () => { const [loading, setLoading] = useState(false); // 源数据 const [dataTable, setDataTable] = useState([]); // 源数据 const [visitedCount, setVisitedCount] = useState([]); // 访问量,统计数据 const [pathCount, setPathCount] = useState([]); // 接口调用次数,统计数据 const [reponseTime, setReponseTime] = useState([]); // 接口调用时长,统计数据 const [timeInterval, setTimeInterval] = useState('3'); // 时间间隔,1/2/3/4(每分钟/5分钟/小时/天),默认每小时 // const [scale, setScale] = useState({}); // 坐标轴别名 // const [pageSize, setPageSize] = useState(100); // 分页大小 const [startTime, setStartTime] = useState(moment().startOf('day')); // 默认值,当天0点00:00:00 const [endTime, setEndTime] = useState( moment(new Date(), 'YYYY-MM-DD HH:mm:ss'), // 默认值,当前时间 ); const [logType, setLogType] = useState(0); // 请求参数,日志类型,默认是正常,0:成功 -1:错误 9999:全部 const [searchWord, setSearchWord] = useState(''); // 关键字 // 计算时间间隔(分钟) const start = new Date(startTime.format('YYYY-MM-DD HH:mm:ss')).getTime(); const end = new Date(endTime.format('YYYY-MM-DD HH:mm:ss')).getTime(); const minuteInterval = (end - start) / (60 * 1000); // 相隔多少分钟 if (minuteInterval <= 0) { notification.error({ message: '时间设置有误', description: '起始时间应该早于结束时间', }); } const countInterval = () => { if (minuteInterval > 0 && minuteInterval <= 30) { setTimeInterval('1'); } else if (minuteInterval > 30 && minuteInterval <= 120) { setTimeInterval('2'); } else if (minuteInterval > 120 && minuteInterval <= 60 * 24) { setTimeInterval('3'); } else { setTimeInterval('4'); } }; const columns = [ { title: '接口名称', dataIndex: 'Path', key: 'Path', fixed: 'left', }, { title: '调用时间', dataIndex: 'CallTime', key: 'CallTime', }, { title: 'IP', dataIndex: 'IP', key: 'IP', }, { title: '返回状态', dataIndex: 'Result', key: 'Result', render: record => { if (record === 0) { return '正常'; } return '错误'; }, }, { title: '错误信息', dataIndex: 'ErrorMsg', key: 'ErrorMsg', render: record => { if (!record) { return '-'; } return record; }, }, { title: '请求方法', dataIndex: 'Method', key: 'Method', }, { title: '查询参数', dataIndex: 'QueryString', key: 'QueryString', }, { title: '请求体', dataIndex: 'Body', key: 'Body', }, { title: '耗时/ms', dataIndex: 'ConsumerTime', key: 'ConsumerTime', fixed: 'right', defaultSortOrder: 'descend', sorter: (a, b) => a.ConsumerTime - b.ConsumerTime, }, { title: '返回体大小/byte', dataIndex: 'ResponseSize', key: 'ResponseSize', fixed: 'right', sorter: (a, b) => a.ResponseSize - b.ResponseSize, }, ]; // 在起止时间任意一个变化后获取数据,且起止时间应该早于结束时间 useEffect(() => { if (startTime && endTime && end - start > 0) { countInterval(); // 根据起止时间计算时间间隔 } }, [startTime, endTime]); useEffect(() => { if (startTime && endTime && end - start > 0) { setLoading(true); getData('/TrafficStatistics', setVisitedCount); // 访问量统计 } }, [startTime, endTime, logType, timeInterval]); useEffect(() => { if (startTime && endTime && end - start > 0) { setLoading(true); getData('/TopCountList', setPathCount); // 接口调用频次统计 getData('/TopConsumeList', setReponseTime); // 接口平均耗时统计 getData('/GetOMSLog', setDataTable); // 接口调用记录 } }, [startTime, endTime, logType]); // 封装接口请求,参数url/设置方法set const getData = (url, set) => { post(`${PUBLISH_SERVICE}/LogCenter${url}`, { // 获取日志表数据时PageSize设置为200,其他接口默认值20 PageSize: url === '/GetOMSLog' ? 200 : 20, DateFrom: startTime.format('YYYY-MM-DD HH:mm:ss'), DateTo: endTime.format('YYYY-MM-DD HH:mm:ss'), IP: '', Module: url === '/GetOMSLog' ? searchWord : '', LogType: +logType, Description: '', LoginName: '', UserName: '', StaticsType: +timeInterval, }) .then(res => { if (res.code === 0) { if (!res.data) { set([]); } else { set( res.data.map((item, index) => { item.key = index; if (url === '/TrafficStatistics') { item.StartTime = item.StartTime.replace(' ', '-'); } return item; }), ); } } else { notification.error({ message: '数据获取失败', description: res.message, }); } setLoading(false); }) .catch(err => { message.error(err); setLoading(false); }); }; // DatePicker改变点击确定时 const changeStartTime = time => { setStartTime(time); }; const changeEndTime = time => { setEndTime(time); }; // 近1/6/12/24小时,同时设置对应的时间间隔 const setTime = (time, value) => { setTimeInterval(value); setEndTime(moment(new Date(), 'YYYY-MM-DD HH:mm:ss')); setStartTime( moment( new Date(new Date().getTime() - time * 60 * 1000), 'YYYY-MM-DD HH:mm:ss', ), ); }; // 设置返回状态 const changeStatus = value => { setLogType(value); }; // 设置时间间隔 const selectChange = value => { setTimeInterval(value); }; // 获取搜索框的值 const handleSearch = e => { setSearchWord(e.target.value); // console.log(e.target.value); }; return ( <> <div className={styles.serviceLog}> <Row className={styles.head}> <Col span={24}> <span>时间:</span> <DatePicker showTime format="YYYY-MM-DD HH:mm:ss" placeholder="起始时间" value={startTime} onChange={changeStartTime} allowClear={false} /> <SwapRightOutlined style={{ verticalAlign: '0.125em' }} /> <DatePicker showTime format="YYYY-MM-DD HH:mm:ss" placeholder="结束时间" value={endTime} onChange={changeEndTime} style={{ marginRight: '10px' }} allowClear={false} /> <Button onClick={() => setTime(15, '1')}>近15分钟</Button> <Button onClick={() => setTime(60, '2')}>近1小时</Button> <Button onClick={() => setTime(12 * 60, '3')}>近12小时</Button> <Button onClick={() => setTime(24 * 60, '3')}>近1天</Button> <Button onClick={() => setTime(24 * 7 * 60, '4')}>近1周</Button> <span style={{ marginLeft: '20px' }}>返回状态:</span> <Select defaultValue="正常" onChange={changeStatus}> <Option value="9999">全部</Option> <Option value="0">正常</Option> <Option value="-1">错误</Option> </Select> <Search allowClear style={{ width: 200, marginLeft: '20px' }} placeholder="请输入接口名称" onSearch={() => { getData('/GetOMSLog', setDataTable); }} onChange={e => handleSearch(e)} enterButton value={searchWord} /> </Col> </Row> <Spin spinning={loading} tip="loading"> <Row style={{ background: 'white' }}> <Col offset={6} style={{ paddingTop: '8px' }}> <span>间隔:</span> <Select defaultValue="每小时" value={timeInterval} size="small" onChange={selectChange} > <Option value="1">每分钟</Option> <Option value="2">每5分钟</Option> <Option value="3">每小时</Option> <Option value="4">每天</Option> </Select> </Col> </Row> <Row className={styles.chart}> <Col span={8}> <Chart height={300} autoFit data={visitedCount} interactions={['active-region']} padding="auto" renderer="svg" scale={{ Count: { alias: '计数' }, StartTime: { alias: '访问量统计' }, }} > <Axis name="StartTime" label="" title={{ offset: 20 }} /> <Axis name="Count" title /> <Line shape="smooth" position="StartTime*Count" /> <Tooltip shared /> </Chart> </Col> <Col span={7} offset={1}> <Chart height={300} autoFit data={pathCount} interactions={['active-region']} padding="auto" renderer="svg" scale={{ Count: { alias: '计数' }, Path: { alias: '接口调用频次统计' }, }} > <Axis name="Path" label="" title={{ offset: 20 }} /> <Axis name="Count" title /> <Interval position="Path*Count" /> <Tooltip shared /> </Chart> </Col> <Col span={7} offset={1}> <Chart height={300} autoFit data={reponseTime} interactions={['active-region']} padding="auto" renderer="svg" scale={{ AvgTime: { alias: '响应时长/ms' }, Path: { alias: '接口平均耗时统计' }, }} > <Axis name="Path" label="" title={{ offset: 20 }} /> <Axis name="AvgTime" title /> <Interval position="Path*AvgTime" /> <Tooltip shared /> </Chart> </Col> </Row> <div className={styles.table}> <Table size="small" bordered columns={columns} dataSource={dataTable} scroll={{ x: 'max-content' }} pagination={{ showTotal: (total, range) => `第${range[0]}-${range[1]} 条/共 ${total} 条`, pageSizeOptions: [10, 20, 50, 100], defaultPageSize: 20, showQuickJumper: true, showSizeChanger: true, }} /> </div> </Spin> </div> </> ); }; export default ServiceLog;