mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-09-12 17:46:20 +08:00
feat: 网站日志支持读取 10M 以上文件 (#2072)
This commit is contained in:
parent
680e93dcd4
commit
9f0d957145
6 changed files with 103 additions and 33 deletions
|
@ -141,9 +141,11 @@ type WebsiteNginxUpdate struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteLogReq struct {
|
type WebsiteLogReq struct {
|
||||||
ID uint `json:"id" validate:"required"`
|
ID uint `json:"id" validate:"required"`
|
||||||
Operate string `json:"operate" validate:"required"`
|
Operate string `json:"operate" validate:"required"`
|
||||||
LogType string `json:"logType" validate:"required"`
|
LogType string `json:"logType" validate:"required"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteDefaultUpdate struct {
|
type WebsiteDefaultUpdate struct {
|
||||||
|
|
|
@ -41,6 +41,7 @@ type WebsiteHTTPS struct {
|
||||||
type WebsiteLog struct {
|
type WebsiteLog struct {
|
||||||
Enable bool `json:"enable"`
|
Enable bool `json:"enable"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
|
End bool `json:"end"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PHPConfig struct {
|
type PHPConfig struct {
|
||||||
|
|
|
@ -931,19 +931,12 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filePath := path.Join(sitePath, "log", req.LogType)
|
filePath := path.Join(sitePath, "log", req.LogType)
|
||||||
fileInfo, err := os.Stat(filePath)
|
lines, end, err := files.ReadFileByLine(filePath, req.Page, req.PageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if fileInfo.Size() > 20<<20 {
|
res.End = end
|
||||||
return nil, buserr.New(constant.ErrFileToLarge)
|
res.Content = strings.Join(lines, "\n")
|
||||||
}
|
|
||||||
fileInfo.Size()
|
|
||||||
content, err := os.ReadFile(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res.Content = string(content)
|
|
||||||
return res, nil
|
return res, nil
|
||||||
case constant.DisableLog:
|
case constant.DisableLog:
|
||||||
key := "access_log"
|
key := "access_log"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package files
|
package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -75,3 +76,36 @@ const dotCharacter = 46
|
||||||
func IsHidden(path string) bool {
|
func IsHidden(path string) bool {
|
||||||
return path[0] == dotCharacter
|
return path[0] == dotCharacter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReadFileByLine(filename string, page, pageSize int) ([]string, bool, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
var lines []string
|
||||||
|
currentLine := 0
|
||||||
|
startLine := (page - 1) * pageSize
|
||||||
|
endLine := startLine + pageSize
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
if currentLine >= startLine && currentLine < endLine {
|
||||||
|
lines = append(lines, scanner.Text())
|
||||||
|
}
|
||||||
|
currentLine++
|
||||||
|
if currentLine >= endLine {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
isEndOfFile := currentLine < endLine
|
||||||
|
|
||||||
|
return lines, isEndOfFile, nil
|
||||||
|
}
|
||||||
|
|
|
@ -82,11 +82,14 @@ export namespace Website {
|
||||||
id: number;
|
id: number;
|
||||||
operate: string;
|
operate: string;
|
||||||
logType: string;
|
logType: string;
|
||||||
|
page?: number;
|
||||||
|
pageSize?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WebSiteLog {
|
export interface WebSiteLog {
|
||||||
enable: boolean;
|
enable: boolean;
|
||||||
content: string;
|
content: string;
|
||||||
|
end: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Domain {
|
export interface Domain {
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<codemirror
|
<codemirror
|
||||||
|
re="logContainer"
|
||||||
style="height: calc(100vh - 430px); width: 100%"
|
style="height: calc(100vh - 430px); width: 100%"
|
||||||
:autofocus="true"
|
:autofocus="true"
|
||||||
:placeholder="$t('website.noLog')"
|
:placeholder="$t('website.noLog')"
|
||||||
|
@ -33,7 +34,7 @@
|
||||||
theme="cobalt"
|
theme="cobalt"
|
||||||
:styleActiveLine="true"
|
:styleActiveLine="true"
|
||||||
:extensions="extensions"
|
:extensions="extensions"
|
||||||
v-model="data.content"
|
v-model="content"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
@ready="handleReady"
|
@ready="handleReady"
|
||||||
/>
|
/>
|
||||||
|
@ -43,7 +44,7 @@
|
||||||
import { Codemirror } from 'vue-codemirror';
|
import { Codemirror } from 'vue-codemirror';
|
||||||
import { javascript } from '@codemirror/lang-javascript';
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
import { oneDark } from '@codemirror/theme-one-dark';
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
import { computed, nextTick, onMounted, onUnmounted, ref, shallowRef } from 'vue';
|
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue';
|
||||||
import { OpWebsiteLog } from '@/api/modules/website';
|
import { OpWebsiteLog } from '@/api/modules/website';
|
||||||
import { dateFormatForName, downloadWithContent } from '@/utils/util';
|
import { dateFormatForName, downloadWithContent } from '@/utils/util';
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
|
@ -74,31 +75,52 @@ const tailLog = ref(false);
|
||||||
let timer: NodeJS.Timer | null = null;
|
let timer: NodeJS.Timer | null = null;
|
||||||
|
|
||||||
const view = shallowRef();
|
const view = shallowRef();
|
||||||
|
const editorContainer = ref<HTMLDivElement | null>(null);
|
||||||
const handleReady = (payload) => {
|
const handleReady = (payload) => {
|
||||||
view.value = payload.view;
|
view.value = payload.view;
|
||||||
|
editorContainer.value = payload.container;
|
||||||
};
|
};
|
||||||
|
const content = ref('');
|
||||||
|
const end = ref(false);
|
||||||
|
const lastContent = ref('');
|
||||||
|
|
||||||
|
const readReq = reactive({
|
||||||
|
id: id.value,
|
||||||
|
operate: 'get',
|
||||||
|
logType: logType.value,
|
||||||
|
page: 0,
|
||||||
|
pageSize: 500,
|
||||||
|
});
|
||||||
|
|
||||||
const getContent = () => {
|
const getContent = () => {
|
||||||
const req = {
|
if (!end.value) {
|
||||||
id: id.value,
|
readReq.page += 1;
|
||||||
operate: 'get',
|
}
|
||||||
logType: logType.value,
|
OpWebsiteLog(readReq).then((res) => {
|
||||||
};
|
if (!end.value && res.data.end) {
|
||||||
loading.value = true;
|
lastContent.value = content.value;
|
||||||
OpWebsiteLog(req)
|
}
|
||||||
.then((res) => {
|
data.value = res.data;
|
||||||
data.value = res.data;
|
if (res.data.content != '') {
|
||||||
nextTick(() => {
|
if (end.value) {
|
||||||
const state = view.value.state;
|
content.value = lastContent.value + '\n' + res.data.content;
|
||||||
view.value.dispatch({
|
} else {
|
||||||
selection: { anchor: state.doc.length, head: state.doc.length },
|
if (content.value == '') {
|
||||||
scrollIntoView: true,
|
content.value = res.data.content;
|
||||||
});
|
} else {
|
||||||
|
content.value = content.value + '\n' + res.data.content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end.value = res.data.end;
|
||||||
|
nextTick(() => {
|
||||||
|
const state = view.value.state;
|
||||||
|
view.value.dispatch({
|
||||||
|
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||||
});
|
});
|
||||||
})
|
view.value.focus();
|
||||||
.finally(() => {
|
|
||||||
loading.value = false;
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeTail = () => {
|
const changeTail = () => {
|
||||||
|
@ -152,8 +174,23 @@ const onCloseLog = async () => {
|
||||||
timer = null;
|
timer = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isScrolledToBottom(element: HTMLElement): boolean {
|
||||||
|
return element.scrollTop + element.clientHeight === element.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getContent();
|
getContent();
|
||||||
|
nextTick(() => {
|
||||||
|
let editorElement = editorContainer.value.querySelector('.cm-editor');
|
||||||
|
let scrollerElement = editorElement.querySelector('.cm-scroller') as HTMLElement;
|
||||||
|
if (scrollerElement) {
|
||||||
|
scrollerElement.addEventListener('scroll', function () {
|
||||||
|
if (isScrolledToBottom(scrollerElement)) {
|
||||||
|
getContent();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue