mirror of https://github.com/1Panel-dev/1Panel
perf: 优化运行环境日志卡顿情况 (#6172)
parent
1b92b6cbfa
commit
3ec208dc85
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue