perf: 优化运行环境日志卡顿情况 (#6172)

pull/6183/head
zhengkunwang 2024-08-19 18:04:43 +08:00 committed by GitHub
parent 1b92b6cbfa
commit 3ec208dc85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 100 additions and 27 deletions

View File

@ -36,10 +36,11 @@ type FileWgetRes struct {
} }
type FileLineContent struct { type FileLineContent struct {
Content string `json:"content"` Content string `json:"content"`
End bool `json:"end"` End bool `json:"end"`
Path string `json:"path"` Path string `json:"path"`
Total int `json:"total"` Total int `json:"total"`
Lines []string `json:"lines"`
} }
type FileExist struct { type FileExist struct {

View File

@ -474,11 +474,20 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
if err != nil { if err != nil {
return nil, err return nil, err
} }
if req.Latest && req.Page == 1 && len(lines) < 1000 && total > 1 {
preLines, _, _, err := files.ReadFileByLine(logFilePath, total-1, req.PageSize, false)
if err != nil {
return nil, err
}
lines = append(preLines, lines...)
}
res := &response.FileLineContent{ res := &response.FileLineContent{
Content: strings.Join(lines, "\n"), Content: strings.Join(lines, "\n"),
End: isEndOfFile, End: isEndOfFile,
Path: logFilePath, Path: logFilePath,
Total: total, Total: total,
Lines: lines,
} }
return res, nil return res, nil
} }

View File

@ -29,7 +29,6 @@ import { ReadByLine } from '@/api/modules/files';
import { watch } from 'vue'; import { watch } from 'vue';
const editorRef = ref(); const editorRef = ref();
interface LogProps { interface LogProps {
id?: number; id?: number;
type: string; type: string;
@ -74,10 +73,11 @@ let timer: NodeJS.Timer | null = null;
const tailLog = ref(false); const tailLog = ref(false);
const content = ref(''); const content = ref('');
const end = ref(false); const end = ref(false);
const lastContent = ref('');
const scrollerElement = ref<HTMLElement | null>(null); const scrollerElement = ref<HTMLElement | null>(null);
const minPage = ref(1); const minPage = ref(1);
const maxPage = ref(1); const maxPage = ref(1);
const logs = ref([]);
const isLoading = ref(false);
const readReq = reactive({ const readReq = reactive({
id: 0, id: 0,
@ -114,7 +114,12 @@ const stopSignals = [
'image push successful!', 'image push successful!',
]; ];
const lastLogs = ref([]);
const getContent = (pre: boolean) => { const getContent = (pre: boolean) => {
if (isLoading.value) {
return;
}
emit('update:isReading', true); emit('update:isReading', true);
readReq.id = props.config.id; readReq.id = props.config.id;
readReq.type = props.config.type; readReq.type = props.config.type;
@ -122,37 +127,52 @@ const getContent = (pre: boolean) => {
if (readReq.page < 1) { if (readReq.page < 1) {
readReq.page = 1; readReq.page = 1;
} }
isLoading.value = true;
ReadByLine(readReq).then((res) => { ReadByLine(readReq).then((res) => {
if (!end.value && res.data.end) { if (!end.value && res.data.end) {
lastContent.value = content.value; lastLogs.value = [...logs.value];
} }
res.data.content = res.data.content.replace(/\\u(\w{4})/g, function (match, grp) {
return String.fromCharCode(parseInt(grp, 16));
});
data.value = res.data; data.value = res.data;
if (res.data.content != '') { if (res.data.lines && res.data.lines.length > 0) {
if (stopSignals.some((signal) => res.data.content.endsWith(signal))) { res.data.lines = res.data.lines.map((line) =>
line.replace(/\\u(\w{4})/g, function (match, grp) {
return String.fromCharCode(parseInt(grp, 16));
}),
);
const newLogs = res.data.lines;
if (newLogs.length === readReq.pageSize && readReq.page < res.data.total) {
readReq.page++;
}
if (
readReq.type == 'php' &&
logs.value.length > 0 &&
newLogs.length > 0 &&
newLogs[newLogs.length - 1] === logs.value[logs.value.length - 1]
) {
isLoading.value = false;
return;
}
if (stopSignals.some((signal) => newLogs[newLogs.length - 1].endsWith(signal))) {
onCloseLog(); onCloseLog();
} }
if (end.value) { if (end.value) {
if (lastContent.value == '') { if ((logs.value.length = 0)) {
content.value = res.data.content; logs.value = newLogs;
} else { } else {
content.value = pre logs.value = pre ? [...newLogs, ...lastLogs.value] : [...lastLogs.value, ...newLogs];
? res.data.content + '\n' + lastContent.value
: lastContent.value + '\n' + res.data.content;
} }
} else { } else {
if (content.value == '') { if ((logs.value.length = 0)) {
content.value = res.data.content; logs.value = newLogs;
} else { } else {
content.value = pre logs.value = pre ? [...newLogs, ...logs.value] : [...logs.value, ...newLogs];
? res.data.content + '\n' + content.value
: content.value + '\n' + res.data.content;
} }
} }
} }
end.value = res.data.end; end.value = res.data.end;
emit('update:hasContent', content.value !== ''); emit('update:hasContent', content.value !== '');
nextTick(() => { nextTick(() => {
@ -171,16 +191,53 @@ const getContent = (pre: boolean) => {
maxPage.value = res.data.total; maxPage.value = res.data.total;
minPage.value = res.data.total; minPage.value = res.data.total;
} }
if (logs.value && logs.value.length > 3000) {
logs.value.splice(0, readReq.pageSize);
if (minPage.value > 1) {
minPage.value--;
}
}
isLoading.value = false;
content.value = logs.value.join('\n');
}); });
}; };
function throttle<T extends (...args: any[]) => any>(func: T, limit: number): (...args: Parameters<T>) => void {
let inThrottle: boolean;
let lastFunc: ReturnType<typeof setTimeout>;
let lastRan: number;
return function (this: any, ...args: Parameters<T>) {
if (!inThrottle) {
func.apply(this, args);
lastRan = Date.now();
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if (Date.now() - lastRan >= limit) {
func.apply(this, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
const throttledGetContent = throttle(getContent, 3000);
const search = () => {
throttledGetContent(false);
};
const changeTail = (fromOutSide: boolean) => { const changeTail = (fromOutSide: boolean) => {
if (fromOutSide) { if (fromOutSide) {
tailLog.value = !tailLog.value; tailLog.value = !tailLog.value;
} }
if (tailLog.value) { if (tailLog.value) {
timer = setInterval(() => { timer = setInterval(() => {
getContent(false); search();
}, 1000 * 3); }, 1000 * 3);
} else { } else {
onCloseLog(); onCloseLog();
@ -198,6 +255,7 @@ const onCloseLog = async () => {
tailLog.value = false; tailLog.value = false;
clearInterval(Number(timer)); clearInterval(Number(timer));
timer = null; timer = null;
isLoading.value = false;
}; };
function isScrolledToBottom(element: HTMLElement): boolean { function isScrolledToBottom(element: HTMLElement): boolean {
@ -218,7 +276,7 @@ const init = () => {
changeTail(false); changeTail(false);
} }
readReq.latest = true; readReq.latest = true;
getContent(false); search();
nextTick(() => {}); nextTick(() => {});
}; };
@ -232,9 +290,14 @@ const initCodemirror = () => {
if (editorRef.value) { if (editorRef.value) {
scrollerElement.value = editorRef.value.$el as HTMLElement; scrollerElement.value = editorRef.value.$el as HTMLElement;
scrollerElement.value.addEventListener('scroll', function () { scrollerElement.value.addEventListener('scroll', function () {
if (tailLog.value) {
return;
}
if (isScrolledToBottom(scrollerElement.value)) { if (isScrolledToBottom(scrollerElement.value)) {
readReq.page = maxPage.value; if (maxPage.value > 1) {
getContent(false); readReq.page = maxPage.value;
}
search();
} }
if (isScrolledToTop(scrollerElement.value)) { if (isScrolledToTop(scrollerElement.value)) {
readReq.page = minPage.value - 1; readReq.page = minPage.value - 1;
@ -242,7 +305,7 @@ const initCodemirror = () => {
return; return;
} }
minPage.value = readReq.page; minPage.value = readReq.page;
getContent(true); throttledGetContent(true);
} }
}); });
let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement; let hljsDom = scrollerElement.value.querySelector('.hljs') as HTMLElement;