mirror of
https://github.com/239573049/FastGateway.git
synced 2026-10-17 05:20:30 +08:00
删除日志记录
由于大量的请求导致日志大量写入代理性能极差所以建议将日志在应用服务处理,而代理应该更纯粹的代理服务。
This commit is contained in:
@@ -10,7 +10,6 @@ Gateway提供了基本的管理服务,提供简单的登录授权,和实时
|
||||
- [x] dashboard监控
|
||||
- [x] 静态文件服务代理
|
||||
- [x] 穿透隧道功能
|
||||
- [x] 请求日志记录监控
|
||||
- [ ] 动态插件管理
|
||||
|
||||
## 技术栈
|
||||
|
||||
@@ -44,7 +44,7 @@ public static class TunnelExensions
|
||||
|
||||
var stream = new DuplexHttpStream(context);
|
||||
|
||||
using var reg = lifetime.ApplicationStopping.Register(() => stream.Abort());
|
||||
await using var reg = lifetime.ApplicationStopping.Register(() => stream.Abort());
|
||||
|
||||
// Keep reusing this connection while, it's still open on the backend
|
||||
while (!context.RequestAborted.IsCancellationRequested)
|
||||
|
||||
@@ -4,7 +4,6 @@ import MainLayout from './layout/main-layout';
|
||||
import Home from './pages/home';
|
||||
import 'reset-css';
|
||||
import Gateway from './pages/gateway';
|
||||
import Logger from './pages/logger';
|
||||
import Setting from './pages/settings';
|
||||
import Login from './pages/login';
|
||||
|
||||
@@ -20,10 +19,6 @@ const router = createBrowserRouter([{
|
||||
path: "Gateway",
|
||||
element: <Gateway />
|
||||
},
|
||||
{
|
||||
path: "Logger",
|
||||
element: <Logger />
|
||||
},
|
||||
{
|
||||
path: "Setting",
|
||||
element: <Setting />
|
||||
|
||||
@@ -17,8 +17,6 @@ export default function MainLayout() {
|
||||
setSelectedKeys(['Home']);
|
||||
} else if (path === '/Gateway') {
|
||||
setSelectedKeys(['Gateway']);
|
||||
} else if (path === '/Logger') {
|
||||
setSelectedKeys(['Logger']);
|
||||
} else if (path === '/Setting') {
|
||||
setSelectedKeys(['Setting']);
|
||||
}
|
||||
@@ -35,8 +33,6 @@ export default function MainLayout() {
|
||||
navigate('/');
|
||||
} else if (key.itemKey === 'Gateway') {
|
||||
navigate('/Gateway');
|
||||
} else if (key.itemKey === 'Logger') {
|
||||
navigate('/Logger');
|
||||
} else if (key.itemKey === 'Setting') {
|
||||
navigate('/Setting');
|
||||
}
|
||||
@@ -45,7 +41,6 @@ export default function MainLayout() {
|
||||
items={[
|
||||
{ itemKey: 'Home', text: '首页', icon: <IconHome size="large" /> },
|
||||
{ itemKey: 'Gateway', text: '代理设置', icon: <IconServerStroked /> },
|
||||
{ itemKey: 'Logger', text: '日志', icon: <IconFile size="large" /> },
|
||||
{ itemKey: 'Setting', text: '设置', icon: <IconSetting size="large" /> },
|
||||
]}
|
||||
header={{
|
||||
|
||||
+10
-191
@@ -1,166 +1,9 @@
|
||||
import { Panel } from '../../service/RequestLogService';
|
||||
import { useEffect, useState } from "react";
|
||||
import { VChart } from "@visactor/react-vchart";
|
||||
import { Col, Row } from '@douyinfe/semi-ui';
|
||||
import { stream } from '../../service/NetWorkService';
|
||||
import { debounce } from '../../utils/utils'
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default function Home() {
|
||||
const [hours, setHours] = useState<number>(24);
|
||||
const [requestSize, setRequestSize] = useState<any>({
|
||||
spec: {
|
||||
|
||||
type: 'area',
|
||||
data: {
|
||||
values: [
|
||||
]
|
||||
},
|
||||
title: {
|
||||
visible: true,
|
||||
text: '最近24小时记录'
|
||||
},
|
||||
xField: 'time',
|
||||
yField: 'value'
|
||||
}
|
||||
});
|
||||
|
||||
const [requestStatusCode, setRequestStatusCode] = useState<any>({
|
||||
spec: {
|
||||
type: 'bar',
|
||||
data: {
|
||||
values: [
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
const [requestPath, setRequestPath] = useState<any>({
|
||||
spec: {
|
||||
type: 'pie',
|
||||
data: [
|
||||
{
|
||||
id: 'id0',
|
||||
values: [
|
||||
]
|
||||
}
|
||||
],
|
||||
outerRadius: 0.8,
|
||||
valueField: 'value',
|
||||
categoryField: 'type',
|
||||
title: {
|
||||
visible: true,
|
||||
text: '路由热度排名'
|
||||
},
|
||||
legends: {
|
||||
visible: true,
|
||||
orient: 'left'
|
||||
},
|
||||
label: {
|
||||
visible: true
|
||||
},
|
||||
tooltip: {
|
||||
mark: {
|
||||
content: [
|
||||
{
|
||||
key: (datum: { [x: string]: any; }) => datum['type'],
|
||||
value: (datum: { [x: string]: string; }) => datum['value'] + '%'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function loadingPanel() {
|
||||
const response = await Panel(hours) as any;
|
||||
setRequestSize({
|
||||
spec: {
|
||||
type: 'area',
|
||||
data: {
|
||||
values: response.data.requestSize
|
||||
},
|
||||
title: {
|
||||
visible: true,
|
||||
text: '最近24小时记录'
|
||||
},
|
||||
xField: 'time',
|
||||
yField: 'value'
|
||||
}
|
||||
});
|
||||
setRequestPath({
|
||||
spec: {
|
||||
type: 'pie',
|
||||
data: [
|
||||
{
|
||||
id: 'id0',
|
||||
values: response.data.requestPath
|
||||
}
|
||||
],
|
||||
outerRadius: 0.8,
|
||||
valueField: 'value',
|
||||
categoryField: 'type',
|
||||
title: {
|
||||
visible: true,
|
||||
text: '路由热度排名'
|
||||
},
|
||||
legends: {
|
||||
visible: true,
|
||||
orient: 'left'
|
||||
},
|
||||
label: {
|
||||
visible: true
|
||||
},
|
||||
tooltip: {
|
||||
mark: {
|
||||
content: [
|
||||
{
|
||||
key: (datum: { [x: string]: any; }) => datum['type'],
|
||||
value: (datum: { [x: string]: string; }) => datum['value'] + '%'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
setRequestStatusCode({
|
||||
spec: {
|
||||
type: 'bar',
|
||||
data: {
|
||||
values: response.data.requestStatusCode
|
||||
},
|
||||
xField: ['type', 'country'],
|
||||
yField: 'value',
|
||||
seriesField: 'country',
|
||||
animationAppear: {
|
||||
duration: 1500,
|
||||
easing: 'linear'
|
||||
},
|
||||
legends: [{ visible: true, position: 'middle', orient: 'bottom' }],
|
||||
// @ts-ignore
|
||||
animationAppear: {
|
||||
duration: 500,
|
||||
oneByOne: true
|
||||
},
|
||||
axes: [
|
||||
{
|
||||
orient: 'left',
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const loadingPanelFunction = debounce(loadingPanel, 500, true);
|
||||
loadingPanelFunction()
|
||||
}, [hours]);
|
||||
|
||||
useEffect(() => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
var chartDom = document.getElementById('network')!;
|
||||
var myChart = echarts.init(chartDom);
|
||||
@@ -168,7 +11,7 @@ export default function Home() {
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '实时流量监控'
|
||||
text: '实时流量'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
@@ -291,7 +134,7 @@ export default function Home() {
|
||||
fetchData();
|
||||
|
||||
// 设置定时器,每隔一段时间调用获取数据的函数
|
||||
const intervalId = setInterval(fetchData, 6000); // 每5秒获取一次数据
|
||||
const intervalId = setInterval(fetchData, 5900); // 每5秒获取一次数据
|
||||
|
||||
// 组件卸载时清除定时器
|
||||
return () => {
|
||||
@@ -302,7 +145,14 @@ export default function Home() {
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
|
||||
<h2 style={{
|
||||
textAlign: 'center',
|
||||
margin: '10px 0',
|
||||
fontSize: '20px',
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
数据面板
|
||||
</h2>
|
||||
<Row>
|
||||
<div id='network' style={{
|
||||
width: '100%',
|
||||
@@ -311,37 +161,6 @@ export default function Home() {
|
||||
}}>
|
||||
</div>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={12}><div className="col-content">
|
||||
<VChart
|
||||
spec={{
|
||||
height: 350,
|
||||
...requestSize.spec,
|
||||
}}
|
||||
/></div></Col>
|
||||
<Col span={12}><div className="col-content">
|
||||
<VChart
|
||||
spec={{
|
||||
height: 350,
|
||||
...requestPath.spec,
|
||||
}} /></div></Col>
|
||||
</Row>
|
||||
|
||||
<Row style={{
|
||||
marginTop: '20px',
|
||||
}}>
|
||||
<div style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'bold',
|
||||
marginBottom: '20px',
|
||||
}}>
|
||||
7天请求状态码分布
|
||||
</div>
|
||||
<VChart
|
||||
spec={{
|
||||
...requestStatusCode.spec,
|
||||
}} />
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
import { Button, Input, Pagination, Table, Toast, DatePicker } from "@douyinfe/semi-ui";
|
||||
import { useEffect, useState } from "react";
|
||||
import { RequestLog } from '../../service/RequestLogService'
|
||||
|
||||
|
||||
export default function Logger() {
|
||||
|
||||
const [total, setTotal] = useState<number>(0);
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [input, setInput] = useState({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
keyword: '',
|
||||
startTime: null as any,
|
||||
endTime: null as any,
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '请求地址',
|
||||
dataIndex: 'path',
|
||||
},
|
||||
{
|
||||
title: '请求方式',
|
||||
dataIndex: 'method',
|
||||
},
|
||||
{
|
||||
title: 'IP',
|
||||
dataIndex: 'ip',
|
||||
},
|
||||
{
|
||||
title: '请求状态',
|
||||
dataIndex: 'statusCode',
|
||||
},
|
||||
{
|
||||
title: '请求耗时(ms)',
|
||||
dataIndex: 'executionDuration'
|
||||
},
|
||||
{
|
||||
title: '浏览器信息',
|
||||
dataIndex: 'browserInfo',
|
||||
// 超出隐藏
|
||||
ellipsis: true,
|
||||
|
||||
},
|
||||
{
|
||||
title: '扩展字段',
|
||||
dataIndex: 'extraProperties',
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
loadingData();
|
||||
}, [input]);
|
||||
|
||||
async function loadingData() {
|
||||
try {
|
||||
const data = await RequestLog(input) as any;
|
||||
setData(data.items);
|
||||
setTotal(data.total);
|
||||
} catch (error) {
|
||||
Toast.error('加载失败' + error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
marginBottom: '20px'
|
||||
|
||||
}}>
|
||||
<Input
|
||||
size='large'
|
||||
placeholder="请输入搜索关键词"
|
||||
value={input.keyword}
|
||||
onChange={(e) => {
|
||||
setInput({
|
||||
...input,
|
||||
keyword: e
|
||||
})
|
||||
}}
|
||||
style={{
|
||||
width: '200px',
|
||||
marginRight: '20px',
|
||||
}}
|
||||
/>
|
||||
<DatePicker
|
||||
size='large'
|
||||
placeholder="开始时间"
|
||||
style={{
|
||||
width: '200px',
|
||||
marginRight: '20px',
|
||||
}}
|
||||
value={input.startTime}
|
||||
onChange={(value) => {
|
||||
setInput({
|
||||
...input,
|
||||
startTime: value
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<DatePicker
|
||||
size='large'
|
||||
placeholder="结束时间"
|
||||
style={{
|
||||
width: '200px',
|
||||
marginRight: '20px',
|
||||
}}
|
||||
value={input.endTime}
|
||||
onChange={(value) => {
|
||||
setInput({
|
||||
...input,
|
||||
endTime: value
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<Button size='large' type="primary" block style={{ width: '100px' }}>搜索</Button>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
sticky={{ top: 0 }} style={{
|
||||
height: 'calc(100vh - 300px)',
|
||||
overflow: 'auto',
|
||||
padding: '10px',
|
||||
}} columns={columns} dataSource={data} pagination={false} />
|
||||
<Pagination total={total} style={{ marginBottom: 12 }} pageSize={input.pageSize} currentPage={input.page} onPageChange={(page) => {
|
||||
setInput({
|
||||
...input,
|
||||
page: page
|
||||
})
|
||||
}} onPageSizeChange={(pageSize) => {
|
||||
setInput({
|
||||
...input,
|
||||
pageSize: pageSize
|
||||
})
|
||||
}}></Pagination>
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import { getList ,update} from "../../service/SettingService";
|
||||
export default function Setting() {
|
||||
|
||||
const [maxBodySize, setMaxBodySize] = useState(100);
|
||||
const [maxLogDay, setMaxLogDay] = useState(30);
|
||||
const LogRetentionTime = "Gateway:Setting:LogRetentionTime";
|
||||
|
||||
const maxRequestBodySize = "Gateway:Setting:MaxRequestBodySize";
|
||||
@@ -18,16 +17,10 @@ export default function Setting() {
|
||||
|
||||
// 获取values的数组中你的name为maxRequestBodySize
|
||||
const v1 = values.find((item: any) => item.name === maxRequestBodySize);
|
||||
setMaxBodySize(v1.defaultValue);
|
||||
const v2 = values.find((item: any) => item.name === LogRetentionTime);
|
||||
setMaxLogDay(v2.defaultValue);
|
||||
setMaxBodySize(v1?.defaultValue);
|
||||
}
|
||||
|
||||
function save() {
|
||||
update(LogRetentionTime, maxLogDay)
|
||||
.then(() => {
|
||||
Toast.success('保存成功')
|
||||
})
|
||||
update(maxRequestBodySize, maxBodySize)
|
||||
.then(() => {
|
||||
Toast.success('保存成功')
|
||||
@@ -53,16 +46,14 @@ export default function Setting() {
|
||||
fontWeight: 'bold',
|
||||
marginBottom: '20px',
|
||||
}}>
|
||||
<span>
|
||||
最大请求Body大小(MB)
|
||||
</span>
|
||||
<Input value={maxBodySize} onChange={(e) => {
|
||||
setMaxBodySize(e as any)
|
||||
}} size='large' style={{
|
||||
marginTop: '20px',
|
||||
}} placeholder='最大请求Body大小(MB)'></Input>
|
||||
<Input size='large' max={360} min={10} value={maxLogDay} onChange={(e) => {
|
||||
setMaxLogDay(e as any)
|
||||
}} style={{
|
||||
marginTop: '20px',
|
||||
}} type="number" placeholder='日志保留最大天数'></Input>
|
||||
<Button size='large' style={{
|
||||
marginTop: '20px',
|
||||
}} onClick={()=>save()} block>保存</Button>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import request from "../utils/request";
|
||||
|
||||
/***
|
||||
* 获取请求日志列表
|
||||
* @param hours {number} 时长(小时)
|
||||
* @constructor
|
||||
*/
|
||||
export function Panel(hours:number){
|
||||
return request.get("/api/gateway/panel",{
|
||||
params:{
|
||||
hours
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求日志列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function RequestLog(params:any){
|
||||
return request.get("/api/gateway/request-log",{
|
||||
params
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user