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

feature/1.8-Improve system observability

See merge request apipark/APIPark!364
This commit is contained in:
lichunxian
2025-04-30 15:49:50 +08:00
7 changed files with 259 additions and 111 deletions
@@ -562,7 +562,7 @@ export const REST_SERVICE_LOG_LIST: PageProColumns<LogItem>[] = [
},
{
title: '消费者',
dataIndex: ['consumers', 'name'],
dataIndex: ['consumer', 'name'],
ellipsis: true
},
{
@@ -45,7 +45,7 @@ const ServiceLogs = ({ serviceType }: { serviceType: 'aiService' | 'restService'
/** 当前选中的时间范围 */
const [timeRange, setTimeRange] = useState<TimeRange | undefined>()
/** 默认时间 */
const [defaultTime] = useState<TimeOption>('sevenDays')
const [defaultTime] = useState<TimeOption>('day')
/** 全局状态 */
const { state } = useGlobalContext()
/**
@@ -16,9 +16,10 @@ type ServiceAreaCharProps = {
customClassNames?: string
dataInfo?: AreaChartInfo
height?: number
showAvgLine?: boolean
}
const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCharProps) => {
const ServiceAreaChart = ({ customClassNames, dataInfo, height, showAvgLine }: ServiceAreaCharProps) => {
const chartRef = useRef<ECharts>(null)
const [option, setOption] = useState<EChartsOption | undefined>({})
const [hasData, setHasData] = useState(true)
@@ -159,6 +160,25 @@ const ServiceAreaChart = ({ customClassNames, dataInfo, height }: ServiceAreaCha
itemStyle: {
color: 'rgb(255, 70, 131)'
},
markLine: showAvgLine ? {
silent: false,
symbol: 'none',
lineStyle: {
width: 1,
type: 'dashed'
},
label: {
position: 'insideEndTop',
formatter: '{c}',
color: '#000',
fontSize: 10,
backgroundColor: 'transparent',
padding: [10, 4],
borderRadius: 2,
distance: -5
},
data: [{ type: 'average', name: 'Avg' }]
} : undefined,
areaStyle: {
color: {
type: 'linear',
@@ -13,15 +13,25 @@ export type BarChartInfo = {
value: number[]
}[]
showXAxis?: boolean
inputTokenTotal?: string
outputTokenTotal?: string
request2xxTotal?: string
request4xxTotal?: string
request5xxTotal?: string
traffic2xxTotal?: string
traffic4xxTotal?: string
traffic5xxTotal?: string
}
type ServiceBarCharProps = {
customClassNames?: string
dataInfo?: BarChartInfo
height?: number
showAvgLine?: boolean
showLegendIndicator?: boolean
}
const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharProps) => {
const ServiceBarChar = ({ customClassNames, dataInfo, height, showAvgLine, showLegendIndicator }: ServiceBarCharProps) => {
const chartRef = useRef<ECharts>(null)
const [option, setOption] = useState<EChartsOption | undefined>({})
// 使用从主题配置中导入的默认颜色,而不是硬编码的颜色值
@@ -29,8 +39,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
const [hasData, setHasData] = useState(true)
const tokenMap = {
inputToken: $t('输入 Token'),
outputToken: $t('输出 Token'),
totalToken: $t('总 Token')
outputToken: $t('输出 Token')
}
const setChartOption = (dataInfo: BarChartInfo) => {
const isNumberArray = typeof dataInfo.data[0] !== 'object'
@@ -53,7 +62,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
// 为每个数据系列添加一行
data.forEach((item, index) => {
// 使用与柱状图相同的颜色策略,确保颜色一致性
const color = index < chartColors.length ? chartColors[index] : item.color
const color = item.color ? item.color : index < chartColors.length ? chartColors[index] : detaultColor
const name = tokenMap[item.name as keyof typeof tokenMap] || item.name
const value = item.value[dataInfo.date.indexOf(params.name)] || 0
@@ -97,30 +106,43 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
top: '110px',
containLabel: true
},
tooltip: dataExists ? {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function (params: any) {
// 如果是数组,取第一个参数的name
const param = Array.isArray(params) ? params[0] : params
return tooltipFormatter(param)
}
} : {
show: false // 没有数据时不显示tooltip
},
tooltip: dataExists
? {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function (params: any) {
// 如果是数组,取第一个参数的name
const param = Array.isArray(params) ? params[0] : params
return tooltipFormatter(param)
}
}
: {
show: false // 没有数据时不显示tooltip
},
legend: {
show: false,
show: !isNumberArray,
data: legendData,
right: '10px',
top: '30px',
top: '60px',
itemWidth: 10,
itemHeight: 10,
textStyle: {
color: '#333'
},
icon: 'rect'
icon: 'rect',
formatter: function(name: string): string {
// 这里可以映射或自定义图例文本
const customNames: Record<string, string> = {
'inputToken': `${$t('输入 Token')} ${showLegendIndicator ? `(${dataInfo.inputTokenTotal})` : ''}`,
'outputToken': `${$t('输出 Token')} ${showLegendIndicator ? `(${dataInfo.outputTokenTotal})` : ''}`,
'2xx': `${'2xx'} ${showLegendIndicator ? `(${dataInfo.request2xxTotal || dataInfo.traffic2xxTotal})` : ''}`,
'4xx': `${'4xx'} ${showLegendIndicator ? `(${dataInfo.request4xxTotal || dataInfo.traffic4xxTotal})` : ''}`,
'5xx': `${'5xx'} ${showLegendIndicator ? `(${dataInfo.request5xxTotal || dataInfo.traffic5xxTotal})` : ''}`
};
return customNames[name] || name;
}
},
xAxis: {
type: 'category',
@@ -209,6 +231,27 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
itemStyle: {
color: detaultColor
},
markLine: showAvgLine
? {
silent: false,
symbol: 'none',
lineStyle: {
width: 1,
type: 'dashed'
},
label: {
position: 'insideEndTop',
formatter: '{c}',
color: '#000',
fontSize: 10,
backgroundColor: 'transparent',
padding: [10, 4],
borderRadius: 2,
distance: -5
},
data: [{ type: 'average', name: 'Avg' }]
}
: undefined,
data: dataInfo.data
}
]
@@ -216,12 +259,33 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
name: item.name,
type: 'bar',
stack: '总量',
markLine: showAvgLine
? {
silent: false,
symbol: 'none',
lineStyle: {
width: 1,
type: 'dashed'
},
label: {
position: 'insideEndTop',
formatter: '{c}',
color: '#000',
fontSize: 10,
backgroundColor: 'transparent',
padding: [10, 4],
borderRadius: 2,
distance: -5
},
data: [{ type: 'average', name: 'Avg' }]
}
: undefined,
emphasis: {
focus: 'series'
},
itemStyle: {
// 使用主题中的颜色列表,如果索引超出范围则使用项目自带的颜色
color: index < chartColors.length ? chartColors[index] : item.color
color: item.color ? item.color : index < chartColors.length ? chartColors[index] : detaultColor
},
data: item.value
}))
@@ -232,7 +296,7 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
// 使用深度监听来确保图表数据更新
useEffect(() => {
if (!dataInfo) return
// 直接获取 ECharts 实例并设置选项
const echartsInstance = chartRef.current?.getEchartsInstance()
if (echartsInstance) {
@@ -252,10 +316,10 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
echartsInstance.resize()
}
}
// 添加监听
window.addEventListener('resize', handleResize)
// 组件卸载时移除监听
return () => {
window.removeEventListener('resize', handleResize)
@@ -264,11 +328,11 @@ const ServiceBarChar = ({ customClassNames, dataInfo, height }: ServiceBarCharPr
return (
<div className={`w-full ${customClassNames}`}>
<div style={!hasData ? { cursor: 'default', pointerEvents: 'none' } : {}}>
<ECharts
ref={chartRef}
option={option}
style={{ height: height || 400 }}
opts={{ renderer: 'svg' }}
<ECharts
ref={chartRef}
option={option}
style={{ height: height || 400 }}
opts={{ renderer: 'svg' }}
theme="apipark" // 这里应用主题名称,需要先在应用入口注册
/>
</div>
@@ -22,7 +22,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
/** 面板 loading */
const [dashboardLoading, setDashboardLoading] = useState(true)
/** 默认时间 */
const [defaultTime] = useState<TimeOption>('sevenDays')
const [defaultTime] = useState<TimeOption>('day')
/** 当前选中的时间范围 */
const [timeRange, setTimeRange] = useState<TimeRange | undefined>()
/** 总数数据 */
@@ -73,7 +73,12 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
'avg_token_per_subscriber',
'input_token',
'output_token',
'total_token'
'total_token',
'request_2xx_total',
'request_4xx_total',
'request_5xx_total',
'input_token_total',
'output_token_total'
]
}).then((response) => {
const { code, data, msg } = response
@@ -105,19 +110,29 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
// 设置总数数据
setBarChartInfo([
// 服务请求次数
setBarChartInfoData({
title: $t('请求数'),
data: serviceOverview.requestOverview,
value: serviceOverview.requestTotal,
date: serviceOverview.date
}),
{
...setBarChartInfoData({
title: $t('请求数'),
data: serviceOverview.requestOverview,
value: serviceOverview.requestTotal,
date: serviceOverview.date
}),
request2xxTotal: serviceOverview.request2xxTotal,
request4xxTotal: serviceOverview.request4xxTotal,
request5xxTotal: serviceOverview.request5xxTotal
},
// 流量消耗总数
setBarChartInfoData({
title: $t('流量'),
data: serviceOverview.trafficOverview,
value: serviceOverview.trafficTotal,
date: serviceOverview.date
})
{
...setBarChartInfoData({
title: $t('流量'),
data: serviceOverview.trafficOverview,
value: serviceOverview.trafficTotal,
date: serviceOverview.date
}),
traffic2xxTotal: serviceOverview.traffic2xxTotal,
traffic4xxTotal: serviceOverview.traffic4xxTotal,
traffic5xxTotal: serviceOverview.traffic5xxTotal
}
])
// 设置平均值数据
setPerBarChartInfo([
@@ -167,22 +182,31 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
// 设置总数数据
setBarChartInfo([
// 服务请求次数
setBarChartInfoData({
title: $t('请求数'),
data: serviceOverview.requestOverview,
value: serviceOverview.requestTotal,
date: serviceOverview.date
}),
{
...setBarChartInfoData({
title: $t('请求数'),
data: serviceOverview.requestOverview,
value: serviceOverview.requestTotal,
date: serviceOverview.date
}),
request2xxTotal: serviceOverview.request2xxTotal,
request4xxTotal: serviceOverview.request4xxTotal,
request5xxTotal: serviceOverview.request5xxTotal
},
// token 消耗总数
setBarChartInfoData({
title: $t('Token'),
data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({
inputToken: item.inputToken,
outputToken: item.outputToken
})),
value: serviceOverview.tokenTotal,
date: serviceOverview.date
})
{
...setBarChartInfoData({
title: $t('Token'),
data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({
inputToken: item.inputToken,
outputToken: item.outputToken
})),
value: serviceOverview.tokenTotal,
date: serviceOverview.date
}),
inputTokenTotal: serviceOverview.inputTokenTotal,
outputTokenTotal: serviceOverview.outputTokenTotal
}
])
// 设置平均值数据
setPerBarChartInfo([
@@ -238,7 +262,13 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
'min_response_time',
'avg_response_time',
'avg_request_per_subscriber',
'avg_traffic_per_subscriber'
'avg_traffic_per_subscriber',
'request_2xx_total',
'request_4xx_total',
'request_5xx_total',
'traffic_2xx_total',
'traffic_4xx_total',
'traffic_5xx_total'
]
}).then((response) => {
const { code, data, msg } = response
@@ -324,7 +354,7 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
body: 'py-[15px] px-[0px]'
}}
>
<ServiceBarChar key={index} height={400} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
<ServiceBarChar showLegendIndicator={true} key={index} height={400} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
</Card>
))}
</div>
@@ -343,11 +373,12 @@ const ServiceOverview = ({ serviceType }: { serviceType: 'aiService' | 'restServ
key={index}
height={270}
dataInfo={item}
showAvgLine={true}
customClassNames="flex-1 relative"
></ServiceAreaChart>
</>
) : (
<ServiceBarChar key={index} height={270} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
<ServiceBarChar key={index} height={270} dataInfo={item} showAvgLine={true} customClassNames="flex-1"></ServiceBarChar>
)}
</Card>
))}
@@ -29,25 +29,15 @@ export const setBarChartInfoData = ({ title, value, data, date, showXAxis }: Bar
const keys = Object.keys(data[0])
// 定义颜色映射
const colorMap: Record<string, string> = {
'2xx': '#7EC26A',
'4xx': '#F2CF59',
'5xx': '#F17975',
inputToken:'#7EC26A',
outputToken: '#F2CF59',
totalToken: '#F17975',
'200': '#7EC26A',
'400': '#F2CF59',
'500': '#F17975'
'2xx': '#3ba272',
'4xx': '#ffc404',
'5xx': '#b92325'
}
// 为每个键创建一个数据集
const transformedData = keys.map((key, index) => {
const transformedData = keys.map((key) => {
// 为没有映射颜色的键生成随机颜色
const color =
colorMap[key] ||
`#${Math.floor(Math.random() * 16777215)
.toString(16)
.padStart(6, '0')}`
const color = colorMap[key]
return {
name: key,
@@ -20,7 +20,7 @@ export default function DashboardTotal() {
/** 获取数据 */
const { fetchData } = useFetch()
/** 默认时间 */
const [defaultTime] = useState<TimeOption>('sevenDays')
const [defaultTime] = useState<TimeOption>('day')
/** 当前选中的时间范围 */
const [timeRange, setTimeRange] = useState<TimeRange | undefined>()
/** 当前激活的标签 */
@@ -66,7 +66,12 @@ export default function DashboardTotal() {
'avg_token_per_subscriber',
'input_token',
'output_token',
'total_token'
'total_token',
'request_2xx_total',
'request_4xx_total',
'request_5xx_total',
'input_token_total',
'output_token_total'
]
}).then((response) => {
const { code, data, msg } = response
@@ -98,7 +103,13 @@ export default function DashboardTotal() {
'max_response_time',
'min_response_time',
'avg_request_per_subscriber',
'avg_traffic_per_subscriber'
'avg_traffic_per_subscriber',
'request_2xx_total',
'request_4xx_total',
'request_5xx_total',
'traffic_2xx_total',
'traffic_4xx_total',
'traffic_5xx_total'
]
}).then((response) => {
const { code, data, msg } = response
@@ -120,19 +131,29 @@ export default function DashboardTotal() {
// 设置总数数据
setBarChartInfo([
// 服务请求次数
setBarChartInfoData({
title: $t('请求数'),
data: serviceOverview.requestOverview,
value: serviceOverview.requestTotal,
date: serviceOverview.date
}),
{
...setBarChartInfoData({
title: $t('请求数'),
data: serviceOverview.requestOverview,
value: serviceOverview.requestTotal,
date: serviceOverview.date
}),
request2xxTotal: serviceOverview.request2xxTotal,
request4xxTotal: serviceOverview.request4xxTotal,
request5xxTotal: serviceOverview.request5xxTotal
},
// 流量消耗总数
setBarChartInfoData({
title: $t('流量'),
data: serviceOverview.trafficOverview,
value: serviceOverview.trafficTotal,
date: serviceOverview.date
})
{
...setBarChartInfoData({
title: $t('流量'),
data: serviceOverview.trafficOverview,
value: serviceOverview.trafficTotal,
date: serviceOverview.date
}),
traffic2xxTotal: serviceOverview.traffic2xxTotal,
traffic4xxTotal: serviceOverview.traffic4xxTotal,
traffic5xxTotal: serviceOverview.traffic5xxTotal
}
])
// 设置平均值数据
setPerBarChartInfo([
@@ -170,22 +191,31 @@ export default function DashboardTotal() {
// 设置总数数据
setBarChartInfo([
// 服务请求次数
setBarChartInfoData({
title: $t('请求数'),
data: serviceOverview.requestOverview,
value: serviceOverview.requestTotal,
date: serviceOverview.date
}),
{
...setBarChartInfoData({
title: $t('请求数'),
data: serviceOverview.requestOverview,
value: serviceOverview.requestTotal,
date: serviceOverview.date
}),
request2xxTotal: serviceOverview.request2xxTotal,
request4xxTotal: serviceOverview.request4xxTotal,
request5xxTotal: serviceOverview.request5xxTotal
},
// token 消耗总数
setBarChartInfoData({
title: $t('Token'),
data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({
inputToken: item.inputToken,
outputToken: item.outputToken
})),
value: serviceOverview.tokenTotal,
date: serviceOverview.date
})
{
...setBarChartInfoData({
title: $t('Token'),
data: serviceOverview.tokenOverview.map((item: { inputToken: number; outputToken: number }) => ({
inputToken: item.inputToken,
outputToken: item.outputToken
})),
value: serviceOverview.tokenTotal,
date: serviceOverview.date
}),
inputTokenTotal: serviceOverview.inputTokenTotal,
outputTokenTotal: serviceOverview.outputTokenTotal
}
])
// 设置平均值数据
setPerBarChartInfo([
@@ -331,7 +361,13 @@ export default function DashboardTotal() {
body: 'py-[15px] px-[0px]'
}}
>
<ServiceBarChar key={index} height={400} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
<ServiceBarChar
showLegendIndicator={true}
key={index}
height={400}
dataInfo={item}
customClassNames="flex-1"
></ServiceBarChar>
</Card>
))}
</div>
@@ -349,12 +385,19 @@ export default function DashboardTotal() {
<ServiceAreaChart
key={index}
height={270}
showAvgLine={true}
dataInfo={item}
customClassNames="flex-1 relative"
></ServiceAreaChart>
</>
) : (
<ServiceBarChar key={index} height={270} dataInfo={item} customClassNames="flex-1"></ServiceBarChar>
<ServiceBarChar
key={index}
height={270}
showAvgLine={true}
dataInfo={item}
customClassNames="flex-1"
></ServiceBarChar>
)}
</Card>
))}