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: {
...yAxis.axisLabel,
overflow: 'truncate',
width: 60,
width: 75,
ellipsis: '...'
}
},
+1
View File
@@ -101,6 +101,7 @@ const MixLineBarChart: React.FC<
stack: 'total',
yAxisIndex: 0,
itemStyle: {
...item.itemStyle,
color: item.color
}
};
+30 -4
View File
@@ -30,6 +30,8 @@ class AnsiParser {
private percent: number = 0;
private isComplete: boolean = false;
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 colorMap = {
'30': 'black',
@@ -52,6 +54,8 @@ class AnsiParser {
this.screen = [['']];
this.rawDataRows = 0;
this.uid = this.uid + 1;
this.lines = [];
this.reminder = '';
this.page = 1;
}
@@ -109,7 +113,6 @@ class AnsiParser {
const n = parseInt(match[1] || '1', 10);
const m = parseInt(match[2] || '1', 10);
const command = match[3];
switch (command) {
case 'A':
this.cursorRow = Math.max(0, this.cursorRow - n);
@@ -183,6 +186,28 @@ class AnsiParser {
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> {
if (this.isProcessing) {
return;
@@ -191,11 +216,12 @@ class AnsiParser {
this.isProcessing = true;
while (this.taskQueue.length > 0) {
const input = this.taskQueue.shift();
const input = this.reminder + this.taskQueue.shift();
if (input) {
try {
const result = this.processInput(input);
const result = this.processInputByLine(input);
this.reminder = result.remainder;
if (this.chunked) {
self.postMessage({ result: result.data, lines: result.lines });
} else if (!this.isComplete) {
@@ -220,7 +246,7 @@ class AnsiParser {
this.processQueue();
} else if (this.isComplete && !this.chunked) {
self.postMessage({
result: this.getScreenText(),
result: this.getAllLines(),
percent: this.percent,
isComplete: true
});
@@ -44,7 +44,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
const pageRef = useRef<any>(page);
const totalPageRef = useRef<any>(totalPage);
const isLoadingMoreRef = useRef(false);
const [currentData, setCurrentData] = useState<any[]>([]);
const [currentData, setCurrentPageData] = useState<any[]>([]);
const scrollPosRef = useRef<any>({
pos: 'bottom',
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(() => {
setLoading(false);
isLoadingMoreRef.current = false;
@@ -119,7 +119,7 @@ const ExportData: React.FC<{
init();
} else {
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'),
model_ids: [],
user_ids: []
@@ -50,11 +50,19 @@ const UsageInner: FC<{ maxWidth: number }> = ({ maxWidth }) => {
const topUserNames = topUsers.map((item: any) => {
topUserPrompt.data.push({
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({
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;
});
@@ -85,7 +85,7 @@ const generateValueMap = (list: { timestamp: number; value: number }[]) => {
};
const generateData = (dateRage: string[], valueMap: Map<string, number>) => {
return dateRage.map((date) => {
return dateRage.map((date, index) => {
const value = valueMap.get(date) || 0;
return {
time: date,
@@ -201,15 +201,40 @@ export default function useUseageData<T>(config: {
};
// =========== token usage data ==============
const completionDataList = generateData(
dateRange,
generateValueMap(completionTokenHistory)
);
const promptDataList = generateData(
dateRange,
generateValueMap(promptTokenHistory)
);
const completionData: any = {
name: 'Completion tokens',
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 = {
name: 'Prompt tokens',
color: baseColorMap.baseR3,
data: generateData(dateRange, generateValueMap(promptTokenHistory))
data: promptDataList.map((item, index) => {
return {
...item,
itemStyle: {
borderRadius: [2, 2, 0, 0]
}
};
})
};
return {