Merge branch 'feature/1.8-cx' into 'main'

feature/1.8-Improve system observability

See merge request apipark/APIPark!348
This commit is contained in:
lichunxian
2025-04-29 18:24:17 +08:00
6 changed files with 105 additions and 145 deletions
@@ -599,7 +599,7 @@ export const AI_SERVICE_LOG_LIST: PageProColumns<LogItem>[] = [
},
{
title: '消费者',
dataIndex: ['consumers', 'name'],
dataIndex: ['consumer', 'name'],
ellipsis: true
},
{
@@ -260,46 +260,11 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro
fetchData<BasicResponse<{ log: AIServiceDetailType }>>('service/log/ai', {
method: 'GET',
eoParams: { log: selectedRow?.id, service: serviceId, team: teamId },
eoTransformKeys: ['is_system_consumer', 'log_time'],
eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
eoTransformKeys: ['is_system_consumer', 'log_time']
}).then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
// const result = data.log
const result = {
id: '123',
api: {
id: '222',
name: 'api222'
},
logTime: '2023-01-01 00:00:00',
consumer: {
id: '333',
name: 'consumers333'
},
isSystemConsumer: false,
status: '200',
provider: {
id: '444',
name: 'provider444'
},
model: 'model1',
ip: '1.1.1.1',
request: {
header:
'{\n "mcpServers": {\n "APIPark/test1234": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}',
body: '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}',
origin: '123',
token: 0
},
response: {
header:
'{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}',
body: '{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}',
origin: '312',
token: '333'
}
}
const result = data.log
getAIServiceDescriptionItemsList({
time: result.logTime,
api: result.api.name,
@@ -329,36 +294,11 @@ const LogDetail = ({ selectedRow, serviceType, serviceId, teamId }: LogDetailPro
fetchData<BasicResponse<{ log: RestServiceDetailType }>>('service/log/rest', {
method: 'GET',
eoParams: { log: selectedRow?.id, service: serviceId, team: teamId },
eoTransformKeys: ['is_system_consumer', 'log_time'],
eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
eoTransformKeys: ['is_system_consumer', 'log_time']
}).then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
const result = {
id: '123',
api: {
id: '222',
name: 'api222'
},
logTime: '2023-01-01 00:00:00',
consumer: {
id: '333',
name: 'consumers333'
},
isSystemConsumer: true,
status: '200',
ip: '1.1.1.1',
request: {
header:
'{\n "mcpServers": {\n "APIPark/test1234": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}',
origin: '123'
},
response: {
header:
'{\n "mcpServers": {\n "APIPark/44444": {\n "url": "http://swagger-demo.apinto.com/openapi/v1/mcp/service/c8bc25ca-8855-45cd-8bcc-239195b6c346/sse?apikey={your_api_key}"\n }\n }\n}',
origin: '312'
}
}
const result = data.log
getRestServiceDescriptionItemsList({
time: result.logTime,
api: result.api.name,
@@ -11,7 +11,7 @@ import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
import { useFetch } from '@common/hooks/http'
import LogDetail, { HttpStatusColor } from './LogDetail'
import { useParams } from 'react-router-dom'
import { ActionType } from '@ant-design/pro-components'
import { ActionType, ParamsType } from '@ant-design/pro-components'
import { getTime } from '@dashboard/utils/dashboard'
export type LogItem = {
@@ -66,9 +66,7 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService'
if (x.dataIndex === 'status') {
x.render = (text: any, record: any) => (
<>
<div className="w-full">
{renderStatusWithColor(record.status)}
</div>
<div className="w-full">{renderStatusWithColor(record.status)}</div>
</>
)
}
@@ -79,73 +77,62 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService'
})
}, [state.language])
/**
* 根据状态码返回对应颜色的文本
* @param status 状态
* @returns
*/
const renderStatusWithColor = (status: string | number) => {
// 获取状态码首位数字
const firstDigit = status.toString().charAt(0)
let color = ''
switch (firstDigit) {
case '2':
color = HttpStatusColor.SUCCESS
break
case '4':
color = HttpStatusColor.CLIENT_ERROR
break
case '5':
color = HttpStatusColor.SERVER_ERROR
break
default:
break
}
return color ? <span style={{ color }}>{status}</span> : status
/**
* 根据状态码返回对应颜色的文本
* @param status 状态
* @returns
*/
const renderStatusWithColor = (status: string | number) => {
// 获取状态码首位数字
const firstDigit = status.toString().charAt(0)
let color = ''
switch (firstDigit) {
case '2':
color = HttpStatusColor.SUCCESS
break
case '4':
color = HttpStatusColor.CLIENT_ERROR
break
case '5':
color = HttpStatusColor.SERVER_ERROR
break
default:
break
}
return color ? <span style={{ color }}>{status}</span> : status
}
/**
* 获取 AI 列表数据
* @param dataType
* @returns
*/
const getAiServiceLogList = () => {
const getAiServiceLogList = (
params: ParamsType & {
pageSize?: number | undefined
current?: number | undefined
keyword?: string | undefined
}
) => {
return fetchData<BasicResponse<{ log: LogItem[] }>>(`service/logs/ai`, {
method: 'GET',
eoParams: {
service: serviceId,
team: teamId,
start: timeRange?.start,
end: timeRange?.end
end: timeRange?.end,
page: params?.current,
page_size:params?.pageSize
},
eoTransformKeys: ['log_time', 'response_time', 'token_per_second'],
eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
eoTransformKeys: ['log_time', 'response_time', 'token_per_second']
})
.then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
// 保存数据
return {
data: [
{
id: '123123',
api: {
id: '444',
name: 'api1'
},
ip: '127.0.0.1',
status: 200,
logTime: '2023-01-01 00:00:00',
token: 123,
consumers: {
id: '333',
name: 'consumers333'
},
model: 'GPT444',
tokenPerSecond: '123m/s'
}
],
total: 1,
data: data.logs,
total: data.total,
success: true
}
} else {
@@ -162,44 +149,33 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService'
* @param dataType
* @returns
*/
const getRestServiceLogList = () => {
const getRestServiceLogList = (
params: ParamsType & {
pageSize?: number | undefined
current?: number | undefined
keyword?: string | undefined
}
) => {
console.log('params===', params)
return fetchData<BasicResponse<{ log: LogItem[] }>>(`service/logs/rest`, {
method: 'GET',
eoParams: {
service: serviceId,
team: teamId,
start: timeRange?.start,
end: timeRange?.end
end: timeRange?.end,
page: params?.current,
page_size:params?.pageSize
},
eoTransformKeys: ['log_time', 'response_time', 'token_per_second'],
eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
eoTransformKeys: ['log_time', 'response_time', 'token_per_second']
})
.then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
const data = []
for (let i = 0; i < 100; i++) {
data.push({
id: '123123' + i,
api: {
id: '444' + i,
name: 'api1' + i
},
ip: '127.0.0.1',
status: 200,
logTime: '2023-01-01 00:00:00',
responseTime: '1111-01-01 00:00:00',
traffic: '123',
consumers: {
id: '123' + i,
name: 'consumers222' + i
}
})
}
// 保存数据
return {
data: data,
total: data.length,
data: data.logs,
total: data.total,
success: true
}
} else {
@@ -260,7 +236,13 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService'
id={`${serviceType}_logs`}
columns={[...columns]}
minVirtualHeight={430}
request={async () => (serviceType === 'aiService' ? getAiServiceLogList() : getRestServiceLogList())}
request={async (
params: ParamsType & {
pageSize?: number | undefined
current?: number | undefined
keyword?: string | undefined
}
) => (serviceType === 'aiService' ? getAiServiceLogList(params) : getRestServiceLogList(params))}
onRowClick={(row: LogItem) => handleRowClick(row)}
/>
</div>
@@ -190,6 +190,25 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha
setChartOption(dataInfo)
}
}, [dataInfo, JSON.stringify(dataInfo)])
// 添加窗口大小变化监听,实现自适应
useEffect(() => {
// 定义resize处理函数
const handleResize = () => {
const echartsInstance = chartRef.current?.getEchartsInstance()
if (echartsInstance) {
echartsInstance.resize()
}
}
// 添加监听
window.addEventListener('resize', handleResize)
// 组件卸载时移除监听
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
return (
<div className={`w-full ${customClassNames}`}>
<div className="absolute top-[10px] left-[10px] w-full">
@@ -231,6 +231,25 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
setChartOption(dataInfo)
}
}, [dataInfo, JSON.stringify(dataInfo)])
// 添加窗口大小变化监听,实现自适应
useEffect(() => {
// 定义resize处理函数
const handleResize = () => {
const echartsInstance = chartRef.current?.getEchartsInstance()
if (echartsInstance) {
echartsInstance.resize()
}
}
// 添加监听
window.addEventListener('resize', handleResize)
// 组件卸载时移除监听
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
return (
<div className={`w-full ${customClassNames}`}>
<ECharts ref={chartRef} option={option} style={{ height: height || 400 }} opts={{ renderer: 'svg' }} />
@@ -59,11 +59,11 @@ const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingL
})
}
return (
<div className="flex w-full pb-[10px]">
<div className="flex flex-wrap w-full pb-[10px]">
{Object.keys(topRankingList)?.map((item: any, index: number) => (
<Card
key={index}
className={`flex-1 h-fit rounded-[10px] ${index > 0 ? 'ml-[10px]' : ''}`}
className={`flex-1 min-w-[300px] h-fit rounded-[10px] ${index > 0 ? 'ml-[10px] md:ml-[10px] sm:ml-0 sm:mt-[10px]' : ''}`}
classNames={{
body: 'p-[15px]'
}}
@@ -75,7 +75,7 @@ const RankingList = ({ topRankingList, serviceType }: { topRankingList: RankingL
id={item}
columns={[...columns]}
minVirtualHeight={430}
noScroll
noScroll={true}
request={() => getTableData(item)}
showPagination={false}
tableClass="ranking-list"