perf: do not handle ansi sequence for parsing logs

This commit is contained in:
jialin
2025-06-30 17:07:29 +08:00
parent a64505fcc1
commit 143f6df2c1
8 changed files with 87 additions and 12 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

+1 -1
View File
@@ -53,7 +53,7 @@ const BarChart: React.FC<ChartProps & { maxItems?: number }> = (props) => {
axisLabel: { axisLabel: {
...yAxis.axisLabel, ...yAxis.axisLabel,
overflow: 'truncate', overflow: 'truncate',
width: 60, width: 75,
ellipsis: '...' ellipsis: '...'
} }
}, },
+1
View File
@@ -101,6 +101,7 @@ const MixLineBarChart: React.FC<
stack: 'total', stack: 'total',
yAxisIndex: 0, yAxisIndex: 0,
itemStyle: { itemStyle: {
...item.itemStyle,
color: item.color color: item.color
} }
}; };
+30 -4
View File
@@ -30,6 +30,8 @@ class AnsiParser {
private percent: number = 0; private percent: number = 0;
private isComplete: boolean = false; private isComplete: boolean = false;
private chunked: boolean = true; // true: send data in chunks, false: send all data at once private chunked: boolean = true; // true: send data in chunks, false: send all data at once
private reminder: string = '';
private lines: string[] = [];
private pageSize: number = 500; private pageSize: number = 500;
private colorMap = { private colorMap = {
'30': 'black', '30': 'black',
@@ -52,6 +54,8 @@ class AnsiParser {
this.screen = [['']]; this.screen = [['']];
this.rawDataRows = 0; this.rawDataRows = 0;
this.uid = this.uid + 1; this.uid = this.uid + 1;
this.lines = [];
this.reminder = '';
this.page = 1; this.page = 1;
} }
@@ -109,7 +113,6 @@ class AnsiParser {
const n = parseInt(match[1] || '1', 10); const n = parseInt(match[1] || '1', 10);
const m = parseInt(match[2] || '1', 10); const m = parseInt(match[2] || '1', 10);
const command = match[3]; const command = match[3];
switch (command) { switch (command) {
case 'A': case 'A':
this.cursorRow = Math.max(0, this.cursorRow - n); this.cursorRow = Math.max(0, this.cursorRow - n);
@@ -183,6 +186,28 @@ class AnsiParser {
return result; return result;
} }
private processInputByLine(input: string): {
data: string[];
lines: number;
remainder: string;
} {
const lines = input?.split(/\r?\n/) || [];
const remainder = lines.pop() || '';
// const data = lines.join('\n');
this.rawDataRows += lines.length;
this.lines.push(...lines);
return {
data: this.lines,
lines: this.rawDataRows,
remainder
};
}
private getAllLines() {
return this.lines.join('\n');
}
private async processQueue(): Promise<void> { private async processQueue(): Promise<void> {
if (this.isProcessing) { if (this.isProcessing) {
return; return;
@@ -191,11 +216,12 @@ class AnsiParser {
this.isProcessing = true; this.isProcessing = true;
while (this.taskQueue.length > 0) { while (this.taskQueue.length > 0) {
const input = this.taskQueue.shift(); const input = this.reminder + this.taskQueue.shift();
if (input) { if (input) {
try { try {
const result = this.processInput(input); const result = this.processInputByLine(input);
this.reminder = result.remainder;
if (this.chunked) { if (this.chunked) {
self.postMessage({ result: result.data, lines: result.lines }); self.postMessage({ result: result.data, lines: result.lines });
} else if (!this.isComplete) { } else if (!this.isComplete) {
@@ -220,7 +246,7 @@ class AnsiParser {
this.processQueue(); this.processQueue();
} else if (this.isComplete && !this.chunked) { } else if (this.isComplete && !this.chunked) {
self.postMessage({ self.postMessage({
result: this.getScreenText(), result: this.getAllLines(),
percent: this.percent, percent: this.percent,
isComplete: true isComplete: true
}); });
@@ -44,7 +44,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
const pageRef = useRef<any>(page); const pageRef = useRef<any>(page);
const totalPageRef = useRef<any>(totalPage); const totalPageRef = useRef<any>(totalPage);
const isLoadingMoreRef = useRef(false); const isLoadingMoreRef = useRef(false);
const [currentData, setCurrentData] = useState<any[]>([]); const [currentData, setCurrentPageData] = useState<any[]>([]);
const scrollPosRef = useRef<any>({ const scrollPosRef = useRef<any>({
pos: 'bottom', pos: 'bottom',
page: 1 page: 1
@@ -59,6 +59,21 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
} }
})); }));
const removeBracketsFromLine = (row: string) => {
return row.startsWith('(…)') ? row.slice(3) : row;
};
const setCurrentData = (lines: string[]) => {
const dataList = lines.map((line, index) => {
return {
content: removeBracketsFromLine(line),
uid: `${pageRef.current}-${index}`
};
});
setCurrentPageData(dataList);
};
const debounceLoading = _.debounce(() => { const debounceLoading = _.debounce(() => {
setLoading(false); setLoading(false);
isLoadingMoreRef.current = false; isLoadingMoreRef.current = false;
@@ -119,7 +119,7 @@ const ExportData: React.FC<{
init(); init();
} else { } else {
setQuery({ setQuery({
start_date: dayjs().subtract(30, 'days').format('YYYY-MM-DD'), start_date: dayjs().subtract(29, 'days').format('YYYY-MM-DD'),
end_date: dayjs().format('YYYY-MM-DD'), end_date: dayjs().format('YYYY-MM-DD'),
model_ids: [], model_ids: [],
user_ids: [] user_ids: []
@@ -50,11 +50,19 @@ const UsageInner: FC<{ maxWidth: number }> = ({ maxWidth }) => {
const topUserNames = topUsers.map((item: any) => { const topUserNames = topUsers.map((item: any) => {
topUserPrompt.data.push({ topUserPrompt.data.push({
name: item.username, name: item.username,
value: item.prompt_token_count value: item.prompt_token_count,
itemStyle: {
borderRadius: !item.completion_token_count
? [2, 2, 2, 2]
: [0, 2, 2, 0]
}
}); });
topUserCompletion.data.push({ topUserCompletion.data.push({
name: item.username, name: item.username,
value: item.completion_token_count value: item.completion_token_count,
itemStyle: {
borderRadius: !item.prompt_token_count ? [2, 2, 2, 2] : [2, 0, 0, 2]
}
}); });
return item.username; return item.username;
}); });
@@ -85,7 +85,7 @@ const generateValueMap = (list: { timestamp: number; value: number }[]) => {
}; };
const generateData = (dateRage: string[], valueMap: Map<string, number>) => { const generateData = (dateRage: string[], valueMap: Map<string, number>) => {
return dateRage.map((date) => { return dateRage.map((date, index) => {
const value = valueMap.get(date) || 0; const value = valueMap.get(date) || 0;
return { return {
time: date, time: date,
@@ -201,15 +201,40 @@ export default function useUseageData<T>(config: {
}; };
// =========== token usage data ============== // =========== token usage data ==============
const completionDataList = generateData(
dateRange,
generateValueMap(completionTokenHistory)
);
const promptDataList = generateData(
dateRange,
generateValueMap(promptTokenHistory)
);
const completionData: any = { const completionData: any = {
name: 'Completion tokens', name: 'Completion tokens',
color: baseColorMap.base, color: baseColorMap.base,
data: generateData(dateRange, generateValueMap(completionTokenHistory)) data: completionDataList.map((item, index) => {
return {
...item,
itemStyle: {
borderRadius: !promptDataList[index].value
? [2, 2, 0, 0]
: [0, 0, 0, 0]
}
};
})
}; };
const promptData: any = { const promptData: any = {
name: 'Prompt tokens', name: 'Prompt tokens',
color: baseColorMap.baseR3, color: baseColorMap.baseR3,
data: generateData(dateRange, generateValueMap(promptTokenHistory)) data: promptDataList.map((item, index) => {
return {
...item,
itemStyle: {
borderRadius: [2, 2, 0, 0]
}
};
})
}; };
return { return {