mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-24 02:59:16 +08:00
feat: 面板系统日志增加日期选择,支持追踪读取 (#2361)
This commit is contained in:
parent
017eb3814b
commit
695f3278c3
@ -91,13 +91,34 @@ func (b *BaseApi) CleanLogs(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Logs
|
||||
// @Summary Load system logs
|
||||
// @Description 获取系统日志
|
||||
// @Summary Load system log files
|
||||
// @Description 获取系统日志文件列表
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /logs/system [get]
|
||||
func (b *BaseApi) GetSystemLogs(c *gin.Context) {
|
||||
data, err := logService.LoadSystemLog()
|
||||
// @Router /logs/system/files [get]
|
||||
func (b *BaseApi) GetSystemFiles(c *gin.Context) {
|
||||
data, err := logService.ListSystemLogFile()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Logs
|
||||
// @Summary Load system logs
|
||||
// @Description 获取系统日志
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /logs/system [post]
|
||||
func (b *BaseApi) GetSystemLogs(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
data, err := logService.LoadSystemLog(req.Name)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
|
@ -1,8 +1,12 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@ -19,13 +23,14 @@ type LogService struct{}
|
||||
const logs = "https://resource.fit2cloud.com/installation-log.sh"
|
||||
|
||||
type ILogService interface {
|
||||
ListSystemLogFile() ([]string, error)
|
||||
CreateLoginLog(operation model.LoginLog) error
|
||||
PageLoginLog(search dto.SearchLgLogWithPage) (int64, interface{}, error)
|
||||
|
||||
CreateOperationLog(operation model.OperationLog) error
|
||||
PageOperationLog(search dto.SearchOpLogWithPage) (int64, interface{}, error)
|
||||
|
||||
LoadSystemLog() (string, error)
|
||||
LoadSystemLog(name string) (string, error)
|
||||
|
||||
CleanLogs(logtype string) error
|
||||
}
|
||||
@ -38,6 +43,32 @@ func (u *LogService) CreateLoginLog(operation model.LoginLog) error {
|
||||
return logRepo.CreateLoginLog(&operation)
|
||||
}
|
||||
|
||||
func (u *LogService) ListSystemLogFile() ([]string, error) {
|
||||
logDir := path.Join(global.CONF.System.BaseDir, "1panel/log")
|
||||
var files []string
|
||||
if err := filepath.Walk(logDir, func(pathItem string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() && (strings.HasSuffix(info.Name(), ".log") || strings.HasSuffix(info.Name(), ".log.gz")) {
|
||||
files = append(files, strings.TrimSuffix(info.Name(), ".gz"))
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(files) < 2 {
|
||||
return files, nil
|
||||
}
|
||||
sort.Slice(files, func(i, j int) bool {
|
||||
return files[i] > files[j]
|
||||
})
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (u *LogService) PageLoginLog(req dto.SearchLgLogWithPage) (int64, interface{}, error) {
|
||||
total, ops, err := logRepo.PageLoginLog(
|
||||
req.Page,
|
||||
@ -81,10 +112,16 @@ func (u *LogService) PageOperationLog(req dto.SearchOpLogWithPage) (int64, inter
|
||||
return total, dtoOps, err
|
||||
}
|
||||
|
||||
func (u *LogService) LoadSystemLog() (string, error) {
|
||||
filePath := path.Join(global.CONF.System.DataDir, "log/1Panel.log")
|
||||
func (u *LogService) LoadSystemLog(name string) (string, error) {
|
||||
filePath := path.Join(global.CONF.System.DataDir, "log", name)
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
fileGzPath := path.Join(global.CONF.System.DataDir, "log", name+".gz")
|
||||
if _, err := os.Stat(fileGzPath); err != nil {
|
||||
return "", buserr.New("ErrHttpReqNotFound")
|
||||
}
|
||||
if err := handleGunzip(fileGzPath); err != nil {
|
||||
return "", fmt.Errorf("handle ungzip file %s falied, err: %v", fileGzPath, err)
|
||||
}
|
||||
}
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
|
@ -240,7 +240,7 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() && strings.HasPrefix(info.Name(), "secure") || strings.HasPrefix(info.Name(), "auth") {
|
||||
if !info.IsDir() && (strings.HasPrefix(info.Name(), "secure") || strings.HasPrefix(info.Name(), "auth")) {
|
||||
if !strings.HasSuffix(info.Name(), ".gz") {
|
||||
fileList = append(fileList, sshFileItem{Name: pathItem, Year: info.ModTime().Year()})
|
||||
return nil
|
||||
@ -311,7 +311,7 @@ func (u *SSHService) AnalysisLog(req dto.SearchForAnalysis) ([]dto.SSHLogAnalysi
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() && strings.HasPrefix(info.Name(), "secure") || strings.HasPrefix(info.Name(), "auth") {
|
||||
if !info.IsDir() && (strings.HasPrefix(info.Name(), "secure") || strings.HasPrefix(info.Name(), "auth")) {
|
||||
if !strings.HasSuffix(info.Name(), ".gz") {
|
||||
fileList = append(fileList, pathItem)
|
||||
return nil
|
||||
|
@ -17,7 +17,7 @@ func (s *LogRouter) InitLogRouter(Router *gin.RouterGroup) {
|
||||
operationRouter.POST("/login", baseApi.GetLoginLogs)
|
||||
operationRouter.POST("/operation", baseApi.GetOperationLogs)
|
||||
operationRouter.POST("/clean", baseApi.CleanLogs)
|
||||
operationRouter.GET("/system", baseApi.GetSystemLogs)
|
||||
|
||||
operationRouter.GET("/system/files", baseApi.GetSystemFiles)
|
||||
operationRouter.POST("/system", baseApi.GetSystemLogs)
|
||||
}
|
||||
}
|
||||
|
@ -7465,7 +7465,7 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"/logs/system": {
|
||||
"get": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
@ -7483,6 +7483,25 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/logs/system/files": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取系统日志文件列表",
|
||||
"tags": [
|
||||
"Logs"
|
||||
],
|
||||
"summary": "Load system log files",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/openResty": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -7458,7 +7458,7 @@
|
||||
}
|
||||
},
|
||||
"/logs/system": {
|
||||
"get": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
@ -7476,6 +7476,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/logs/system/files": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取系统日志文件列表",
|
||||
"tags": [
|
||||
"Logs"
|
||||
],
|
||||
"summary": "Load system log files",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/openResty": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -8807,7 +8807,7 @@ paths:
|
||||
tags:
|
||||
- Logs
|
||||
/logs/system:
|
||||
get:
|
||||
post:
|
||||
description: 获取系统日志
|
||||
responses:
|
||||
"200":
|
||||
@ -8817,6 +8817,17 @@ paths:
|
||||
summary: Load system logs
|
||||
tags:
|
||||
- Logs
|
||||
/logs/system/files:
|
||||
get:
|
||||
description: 获取系统日志文件列表
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Load system log files
|
||||
tags:
|
||||
- Logs
|
||||
/openResty:
|
||||
get:
|
||||
description: 获取 OpenResty 配置信息
|
||||
|
@ -10,8 +10,11 @@ export const getLoginLogs = (info: Log.SearchLgLog) => {
|
||||
return http.post<ResPage<Log.OperationLog>>(`/logs/login`, info);
|
||||
};
|
||||
|
||||
export const getSystemLogs = () => {
|
||||
return http.get<string>(`/logs/system`);
|
||||
export const getSystemFiles = () => {
|
||||
return http.get<Array<string>>(`/logs/system/files`);
|
||||
};
|
||||
export const getSystemLogs = (name: string) => {
|
||||
return http.post<string>(`/logs/system`, { name: name });
|
||||
};
|
||||
|
||||
export const cleanLogs = (param: Log.CleanLog) => {
|
||||
|
@ -336,11 +336,6 @@ html {
|
||||
}
|
||||
}
|
||||
|
||||
.no-active-button {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.common-prompt {
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
@ -369,3 +364,11 @@ html {
|
||||
box-shadow: 0 1px 0 0 var(--el-input-border-color) inset, 0 -1px 0 0 var(--el-input-border-color) inset,
|
||||
-1px 0 0 0 var(--el-input-border-color) inset;
|
||||
}
|
||||
|
||||
.tag-button {
|
||||
margin-right: 10px;
|
||||
&.no-active {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
@ -4,13 +4,13 @@
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="16" :md="16" :lg="16" :xl="16">
|
||||
<el-button class="no-active-button" @click="onChangeRoute('OperationLog')">
|
||||
<el-button class="tag-button no-active" @click="onChangeRoute('OperationLog')">
|
||||
{{ $t('logs.operation') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="onChangeRoute('LoginLog')">
|
||||
<el-button class="tag-button" type="primary" @click="onChangeRoute('LoginLog')">
|
||||
{{ $t('logs.login') }}
|
||||
</el-button>
|
||||
<el-button class="no-active-button" @click="onChangeRoute('SystemLog')">
|
||||
<el-button class="tag-button no-active" @click="onChangeRoute('SystemLog')">
|
||||
{{ $t('logs.system') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
@ -4,13 +4,13 @@
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="16" :md="16" :lg="16" :xl="16">
|
||||
<el-button type="primary" @click="onChangeRoute('OperationLog')">
|
||||
<el-button type="primary" class="tag-button" @click="onChangeRoute('OperationLog')">
|
||||
{{ $t('logs.operation') }}
|
||||
</el-button>
|
||||
<el-button class="no-active-button" @click="onChangeRoute('LoginLog')">
|
||||
<el-button class="tag-button no-active" @click="onChangeRoute('LoginLog')">
|
||||
{{ $t('logs.login') }}
|
||||
</el-button>
|
||||
<el-button class="no-active-button" @click="onChangeRoute('SystemLog')">
|
||||
<el-button class="tag-button no-active" @click="onChangeRoute('SystemLog')">
|
||||
{{ $t('logs.system') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
@ -4,25 +4,36 @@
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
<el-button class="no-active-button" @click="onChangeRoute('OperationLog')">
|
||||
<el-button class="tag-button no-active" @click="onChangeRoute('OperationLog')">
|
||||
{{ $t('logs.operation') }}
|
||||
</el-button>
|
||||
<el-button class="no-active-button" @click="onChangeRoute('LoginLog')">
|
||||
<el-button class="tag-button no-active" @click="onChangeRoute('LoginLog')">
|
||||
{{ $t('logs.login') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="onChangeRoute('SystemLog')">
|
||||
<el-button class="tag-button" type="primary" @click="onChangeRoute('SystemLog')">
|
||||
{{ $t('logs.system') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<template #search>
|
||||
<el-select class="float-left" v-model="currentFile" @change="search()">
|
||||
<template #prefix>{{ $t('commons.button.log') }}</template>
|
||||
<el-option v-for="(item, index) in fileList" :key="index" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<div class="watchCheckbox">
|
||||
<el-checkbox border @change="changeWatch" v-model="isWatch">
|
||||
{{ $t('commons.button.watch') }}
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</template>
|
||||
<template #main>
|
||||
<codemirror
|
||||
:autofocus="true"
|
||||
:placeholder="$t('commons.msg.noneData')"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="height: calc(100vh - 290px)"
|
||||
style="height: calc(100vh - 370px)"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
@ -41,12 +52,16 @@
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { nextTick, onMounted, ref, shallowRef } from 'vue';
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref, shallowRef } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { getSystemLogs } from '@/api/modules/log';
|
||||
import { getSystemFiles, getSystemLogs } from '@/api/modules/log';
|
||||
const router = useRouter();
|
||||
|
||||
const loading = ref();
|
||||
const isWatch = ref();
|
||||
const currentFile = ref('1Panel.log');
|
||||
const fileList = ref();
|
||||
|
||||
const extensions = [javascript(), oneDark];
|
||||
const logs = ref();
|
||||
const view = shallowRef();
|
||||
@ -54,11 +69,31 @@ const handleReady = (payload) => {
|
||||
view.value = payload.view;
|
||||
};
|
||||
|
||||
const loadSystemlogs = async () => {
|
||||
await getSystemLogs()
|
||||
let timer: NodeJS.Timer | null = null;
|
||||
|
||||
const changeWatch = async () => {
|
||||
if (isWatch.value) {
|
||||
timer = setInterval(() => {
|
||||
search();
|
||||
}, 1000 * 3);
|
||||
} else {
|
||||
if (timer) {
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loadFiles = async () => {
|
||||
const res = await getSystemFiles();
|
||||
fileList.value = res.data || [];
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
await getSystemLogs(currentFile.value)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
logs.value = res.data;
|
||||
logs.value = res.data.replace(/\u0000/g, '');
|
||||
nextTick(() => {
|
||||
const state = view.value.state;
|
||||
view.value.dispatch({
|
||||
@ -76,7 +111,22 @@ const onChangeRoute = async (addr: string) => {
|
||||
router.push({ name: addr });
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadSystemlogs();
|
||||
loadFiles();
|
||||
search();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.watchCheckbox {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 10px;
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user