refactor: 统一日志清理

This commit is contained in:
耗子
2026-04-27 20:29:50 +08:00
parent cd64dc65f3
commit 909556b3d6
29 changed files with 208 additions and 188 deletions
+4
View File
@@ -156,6 +156,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
@@ -336,6 +338,8 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ
github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
-22
View File
@@ -40,9 +40,7 @@ func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Post("/clear_log", s.ClearLog)
r.Get("/slow_log", s.SlowLog)
r.Post("/clear_slow_log", s.ClearSlowLog)
r.Get("/root_password", s.GetRootPassword)
r.Post("/root_password", s.SetRootPassword)
r.Get("/config_tune", s.GetConfigTune)
@@ -194,31 +192,11 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
service.Success(w, load)
}
// ClearLog 清空日志
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
if err := systemctl.LogClear("mysqld"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// SlowLog 获取慢查询日志
func (s *App) SlowLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/mysql/mysql-slow.log", app.Root))
}
// ClearSlowLog 清空慢查询日志
func (s *App) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("cat /dev/null > %s/server/mysql/mysql-slow.log", app.Root); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetRootPassword 获取root密码
func (s *App) GetRootPassword(w http.ResponseWriter, r *http.Request) {
rootPassword, err := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
+5 -10
View File
@@ -4,8 +4,8 @@ import (
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
@@ -41,7 +41,6 @@ func (s *App) Route(r chi.Router) {
r.Post("/user_config", s.UpdateUserConfig)
r.Get("/load", s.Load)
r.Get("/log", s.Log)
r.Post("/clear_log", s.ClearLog)
r.Get("/postgres_password", s.GetPostgresPassword)
r.Post("/postgres_password", s.SetPostgresPassword)
r.Get("/config_tune", s.GetConfigTune)
@@ -175,19 +174,15 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
service.Success(w, data)
}
// Log 获取日志
// Log 获取应用日志路径列表
func (s *App) Log(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/postgresql/logs/postgresql-%s.log", app.Root, time.Now().Format(time.DateOnly)))
}
// ClearLog 清空日志
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("rm -rf %s/server/postgresql/logs/postgresql-*.log", app.Root); err != nil {
paths, err := filepath.Glob(fmt.Sprintf("%s/server/postgresql/logs/postgresql-*.log", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
service.Success(w, paths)
}
// GetPostgresPassword 获取 postgres 用户密码
-41
View File
@@ -37,7 +37,6 @@ func NewApp(t *gotext.Locale) *App {
func (s *App) Route(r chi.Router) {
r.Get("/service", s.Service)
r.Post("/clear_log", s.ClearLog)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Get("/processes", s.Processes)
@@ -45,7 +44,6 @@ func (s *App) Route(r chi.Router) {
r.Post("/processes/{process}/stop", s.StopProcess)
r.Post("/processes/{process}/restart", s.RestartProcess)
r.Get("/processes/{process}/log", s.ProcessLog)
r.Post("/processes/{process}/clear_log", s.ClearProcessLog)
r.Get("/processes/{process}", s.ProcessConfig)
r.Post("/processes/{process}", s.UpdateProcessConfig)
r.Delete("/processes/{process}", s.DeleteProcess)
@@ -57,16 +55,6 @@ func (s *App) Service(w http.ResponseWriter, r *http.Request) {
service.Success(w, s.name)
}
// ClearLog 清空日志
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf(`cat /dev/null > /var/log/supervisor/supervisord.log`); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetConfig 获取配置
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
var config string
@@ -227,35 +215,6 @@ func (s *App) ProcessLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, logPath)
}
// ClearProcessLog 清空进程日志
func (s *App) ClearProcessLog(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ProcessName](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
name := programName(req.Process)
var logPath string
if os.IsRHEL() {
logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, name)
} else {
logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, name)
}
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get log path for process %s: %v", req.Process, err))
return
}
if _, err = shell.Execf(`cat /dev/null > '%s'`, logPath); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// ProcessConfig 获取进程配置
func (s *App) ProcessConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ProcessName](r)
-1
View File
@@ -45,7 +45,6 @@ type WebsiteRepo interface {
Create(ctx context.Context, req *request.WebsiteCreate) (*Website, error)
Update(ctx context.Context, req *request.WebsiteUpdate) error
Delete(ctx context.Context, req *request.WebsiteDelete) error
ClearLog(id uint) error
UpdateRemark(id uint, remark string) error
ResetConfig(id uint) error
UpdateStatus(id uint, status bool) error
-10
View File
@@ -828,16 +828,6 @@ func (r *websiteRepo) Delete(ctx context.Context, req *request.WebsiteDelete) er
return r.reloadWebServer()
}
func (r *websiteRepo) ClearLog(id uint) error {
website := new(biz.Website)
if err := r.db.Where("id", id).First(website).Error; err != nil {
return err
}
_, err := shell.Execf(`cat /dev/null > %s/sites/%s/log/access.log`, app.Root, website.Name)
return err
}
func (r *websiteRepo) UpdateRemark(id uint, remark string) error {
website := new(biz.Website)
if err := r.db.Where("id", id).First(website).Error; err != nil {
+2 -3
View File
@@ -264,7 +264,6 @@ func (route *Http) Register(r *chi.Mux) {
r.Get("/{id}", route.website.Get)
r.Put("/{id}", route.website.Update)
r.Delete("/{id}", route.website.Delete)
r.Delete("/{id}/log", route.website.ClearLog)
r.Post("/{id}/update_remark", route.website.UpdateRemark)
r.Post("/{id}/reset_config", route.website.ResetConfig)
r.Post("/{id}/status", route.website.UpdateStatus)
@@ -432,8 +431,6 @@ func (route *Http) Register(r *chi.Mux) {
r.Get("/{version}/load", route.environmentPHP.Load)
r.Get("/{version}/log", route.environmentPHP.Log)
r.Get("/{version}/slow_log", route.environmentPHP.SlowLog)
r.Post("/{version}/clear_log", route.environmentPHP.ClearLog)
r.Post("/{version}/clear_slow_log", route.environmentPHP.ClearSlowLog)
r.Get("/{version}/modules", route.environmentPHP.ModuleList)
r.Post("/{version}/modules", route.environmentPHP.InstallModule)
r.Delete("/{version}/modules", route.environmentPHP.UninstallModule)
@@ -556,6 +553,7 @@ func (route *Http) Register(r *chi.Mux) {
r.Post("/create", route.file.Create)
r.Get("/content", route.file.Content)
r.Post("/save", route.file.Save)
r.Post("/truncate", route.file.Truncate)
r.Post("/delete", route.file.Delete)
r.Post("/upload", route.file.Upload)
r.Post("/exist", route.file.Exist)
@@ -606,6 +604,7 @@ func (route *Http) Register(r *chi.Mux) {
r.Post("/reload", route.systemctl.Reload)
r.Post("/start", route.systemctl.Start)
r.Post("/stop", route.systemctl.Stop)
r.Post("/clear_log", route.systemctl.ClearLog)
})
r.Route("/toolbox_network", func(r chi.Router) {
-38
View File
@@ -249,44 +249,6 @@ func (s *EnvironmentPHPService) SlowLog(w http.ResponseWriter, r *http.Request)
Success(w, fmt.Sprintf("%s/server/php/%d/var/log/slow.log", app.Root, req.Version))
}
func (s *EnvironmentPHPService) ClearLog(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.EnvironmentPHPVersion](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if !s.environmentRepo.IsInstalled("php", fmt.Sprintf("%d", req.Version)) {
Error(w, http.StatusUnprocessableEntity, s.t.Get("PHP-%d is not installed", req.Version))
return
}
if _, err = shell.Execf("cat /dev/null > %s/server/php/%d/var/log/php-fpm.log", app.Root, req.Version); err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, nil)
}
func (s *EnvironmentPHPService) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.EnvironmentPHPVersion](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if !s.environmentRepo.IsInstalled("php", fmt.Sprintf("%d", req.Version)) {
Error(w, http.StatusUnprocessableEntity, s.t.Get("PHP-%d is not installed", req.Version))
return
}
if _, err = shell.Execf("cat /dev/null > %s/server/php/%d/var/log/slow.log", app.Root, req.Version); err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, nil)
}
func (s *EnvironmentPHPService) ModuleList(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.EnvironmentPHPVersion](r)
if err != nil {
+16
View File
@@ -106,6 +106,22 @@ func (s *FileService) Content(w http.ResponseWriter, r *http.Request) {
})
}
// Truncate 截断文件至 0 长度(保留文件本身和元数据)
func (s *FileService) Truncate(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.FilePath](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = stdos.Truncate(req.Path, 0); err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, nil)
}
func (s *FileService) Save(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.FileSave](r)
if err != nil {
+16
View File
@@ -140,3 +140,19 @@ func (s *SystemctlService) Stop(w http.ResponseWriter, r *http.Request) {
Success(w, nil)
}
// ClearLog 清空指定 systemd 服务的 journald 日志
func (s *SystemctlService) ClearLog(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.SystemctlService](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = systemctl.LogClear(req.Service); err != nil {
Error(w, http.StatusInternalServerError, s.t.Get("failed to clear log for %s service: %v", req.Service, err))
return
}
Success(w, nil)
}
-15
View File
@@ -177,21 +177,6 @@ func (s *WebsiteService) Delete(w http.ResponseWriter, r *http.Request) {
Success(w, nil)
}
func (s *WebsiteService) ClearLog(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.ID](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = s.websiteRepo.ClearLog(req.ID); err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, nil)
}
func (s *WebsiteService) UpdateRemark(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.WebsiteUpdateRemark](r)
if err != nil {
-4
View File
@@ -7,12 +7,8 @@ export default {
config: (): any => http.Get('/apps/mariadb/config'),
// 保存配置
saveConfig: (config: string): any => http.Post('/apps/mariadb/config', { config }),
// 清空日志
clearLog: (): any => http.Post('/apps/mariadb/clear_log'),
// 获取慢查询日志
slowLog: (): any => http.Get('/apps/mariadb/slow_log'),
// 清空慢查询日志
clearSlowLog: (): any => http.Post('/apps/mariadb/clear_slow_log'),
// 获取 root 密码
rootPassword: (): any => http.Get('/apps/mariadb/root_password'),
// 修改 root 密码
-4
View File
@@ -7,12 +7,8 @@ export default {
config: (): any => http.Get('/apps/mysql/config'),
// 保存配置
saveConfig: (config: string): any => http.Post('/apps/mysql/config', { config }),
// 清空日志
clearLog: (): any => http.Post('/apps/mysql/clear_log'),
// 获取慢查询日志
slowLog: (): any => http.Get('/apps/mysql/slow_log'),
// 清空慢查询日志
clearSlowLog: (): any => http.Post('/apps/mysql/clear_slow_log'),
// 获取 root 密码
rootPassword: (): any => http.Get('/apps/mysql/root_password'),
// 修改 root 密码
-4
View File
@@ -7,12 +7,8 @@ export default {
config: (): any => http.Get('/apps/percona/config'),
// 保存配置
saveConfig: (config: string): any => http.Post('/apps/percona/config', { config }),
// 清空日志
clearLog: (): any => http.Post('/apps/percona/clear_log'),
// 获取慢查询日志
slowLog: (): any => http.Get('/apps/percona/slow_log'),
// 清空慢查询日志
clearSlowLog: (): any => http.Post('/apps/percona/clear_slow_log'),
// 获取 root 密码
rootPassword: (): any => http.Get('/apps/percona/root_password'),
// 修改 root 密码
-2
View File
@@ -13,8 +13,6 @@ export default {
saveUserConfig: (config: string): any => http.Post('/apps/postgresql/user_config', { config }),
// 获取日志
log: (): any => http.Get('/apps/postgresql/log'),
// 清空错误日志
clearLog: (): any => http.Post('/apps/postgresql/clear_log'),
// 获取 postgres 密码
postgresPassword: (): any => http.Get('/apps/postgresql/postgres_password'),
// 设置 postgres 密码
-5
View File
@@ -5,8 +5,6 @@ export default {
service: (): any => http.Get('/apps/supervisor/service'),
// 获取错误日志
log: (): any => http.Get('/apps/supervisor/log'),
// 清空错误日志
clearLog: (): any => http.Post('/apps/supervisor/clear_log'),
// 获取配置
config: (): any => http.Get('/apps/supervisor/config'),
// 保存配置
@@ -23,9 +21,6 @@ export default {
http.Post(`/apps/supervisor/processes/${process}/restart`),
// 进程日志
processLog: (process: string): any => http.Get(`/apps/supervisor/processes/${process}/log`),
// 清空进程日志
clearProcessLog: (process: string): any =>
http.Post(`/apps/supervisor/processes/${process}/clear_log`),
// 进程配置
processConfig: (process: string): any => http.Get(`/apps/supervisor/processes/${process}`),
// 保存进程配置
@@ -19,12 +19,8 @@ export default {
load: (slug: number): any => http.Get(`/environment/php/${slug}/load`),
// 获取日志
log: (slug: number): any => http.Get(`/environment/php/${slug}/log`),
// 清空日志
clearLog: (slug: number): any => http.Post(`/environment/php/${slug}/clear_log`),
// 获取慢日志
slowLog: (slug: number): any => http.Get(`/environment/php/${slug}/slow_log`),
// 清空慢日志
clearSlowLog: (slug: number): any => http.Post(`/environment/php/${slug}/clear_slow_log`),
// 拓展列表
modules: (slug: number): any => http.Get(`/environment/php/${slug}/modules`),
// 安装拓展
+2
View File
@@ -7,6 +7,8 @@ export default {
content: (path: string): any => http.Get('/file/content', { params: { path } }),
// 保存文件
save: (path: string, content: string): any => http.Post('/file/save', { path, content }),
// 截断文件至 0 长度
truncate: (path: string): any => http.Post('/file/truncate', { path }),
// 删除文件
delete: (path: string): any => http.Post('/file/delete', { path }),
// 上传文件
+3 -1
View File
@@ -16,5 +16,7 @@ export default {
// 启动服务
start: (service: string): any => http.Post('/systemctl/start', { service }),
// 停止服务
stop: (service: string): any => http.Post('/systemctl/stop', { service })
stop: (service: string): any => http.Post('/systemctl/stop', { service }),
// 清空指定服务的 journald 日志
clearLog: (service: string): any => http.Post('/systemctl/clear_log', { service })
}
-2
View File
@@ -19,8 +19,6 @@ export default {
config: (id: number): any => http.Get('/website/' + id),
// 保存网站配置
saveConfig: (id: number, data: any): any => http.Put(`/website/${id}`, data),
// 清空日志
clearLog: (id: number): any => http.Delete('/website/' + id + '/log'),
// 更新备注
updateRemark: (id: number, remark: string): any =>
http.Post(`/website/${id}` + '/update_remark', { remark }),
@@ -203,6 +203,14 @@ onMounted(async () => {
onUnmounted(() => {
closeTerminal()
})
// 由父组件在清空源文件/日志成功后调用,清掉终端 buffer 里残留的旧内容
const clear = () => {
term.value?.clear()
term.value?.reset()
}
defineExpose({ clear })
</script>
<template>
@@ -18,8 +18,13 @@ const props = defineProps({
path: {
type: String,
required: true
},
clearable: {
type: Boolean,
default: false
}
})
const emit = defineEmits<{ clear: [] }>()
const terminalRef = ref<HTMLElement | null>(null)
const term = ref<Terminal | null>(null)
@@ -180,6 +185,14 @@ watch([() => props.path, () => show.value], async () => {
onUnmounted(() => {
closeTerminal()
})
// 由父组件在清空源文件成功后调用,清掉终端 buffer 里残留的旧日志
const clear = () => {
term.value?.clear()
term.value?.reset()
}
defineExpose({ clear })
</script>
<template>
@@ -193,6 +206,16 @@ onUnmounted(() => {
:segmented="false"
@after-leave="closeTerminal"
>
<template v-if="clearable" #header-extra>
<n-popconfirm @positive-click="emit('clear')">
<template #trigger>
<n-button size="small" type="warning">
{{ $gettext('Clear Log') }}
</n-button>
</template>
{{ $gettext('Are you sure you want to clear the log?') }}
</n-popconfirm>
</template>
<div
ref="terminalRef"
class="realtime-log-terminal"
+10 -4
View File
@@ -4,6 +4,8 @@ import { NButton, NDataTable, NInput } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import mysql from '@/api/apps/mysql'
import file from '@/api/panel/file'
import systemctl from '@/api/panel/systemctl'
import ServiceStatus from '@/components/common/ServiceStatus.vue'
import MysqlConfigTuneView from './MysqlConfigTuneView.vue'
@@ -18,6 +20,8 @@ const setRootPasswordLoading = ref(false)
const saveConfigLoading = ref(false)
const clearLogLoading = ref(false)
const clearSlowLogLoading = ref(false)
const runLogRef = ref<{ clear: () => void } | null>(null)
const slowLogRef = ref<{ clear: () => void } | null>(null)
const { data: rootPassword } = useRequest(props.api.rootPassword, {
initialData: ''
@@ -67,8 +71,9 @@ const handleSaveConfig = () => {
const handleClearLog = () => {
clearLogLoading.value = true
useRequest(props.api.clearLog())
useRequest(systemctl.clearLog('mysqld'))
.onSuccess(() => {
runLogRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
.onComplete(() => {
@@ -78,8 +83,9 @@ const handleClearLog = () => {
const handleClearSlowLog = () => {
clearSlowLogLoading.value = true
useRequest(props.api.clearSlowLog())
useRequest(file.truncate(slowLog.value))
.onSuccess(() => {
slowLogRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
.onComplete(() => {
@@ -170,13 +176,13 @@ const handleCopyRootPassword = () => {
<n-button type="primary" :loading="clearLogLoading" :disabled="clearLogLoading" @click="handleClearLog">
{{ $gettext('Clear Log') }}
</n-button>
<realtime-log service="mysqld" />
<realtime-log ref="runLogRef" service="mysqld" />
</n-tab-pane>
<n-tab-pane name="slow-log" :tab="$gettext('Slow Query Log')">
<n-button type="primary" :loading="clearSlowLogLoading" :disabled="clearSlowLogLoading" @click="handleClearSlowLog">
{{ $gettext('Clear Slow Log') }}
</n-button>
<realtime-log :path="slowLog" />
<realtime-log ref="slowLogRef" :path="slowLog" />
</n-tab-pane>
</n-tabs>
</common-page>
+50 -6
View File
@@ -8,6 +8,8 @@ import { NButton, NDataTable } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import postgresql from '@/api/apps/postgresql'
import file from '@/api/panel/file'
import systemctl from '@/api/panel/systemctl'
import ServiceStatus from '@/components/common/ServiceStatus.vue'
import PostgresqlConfigTuneView from './PostgresqlConfigTuneView.vue'
@@ -17,13 +19,33 @@ const setPostgresPasswordLoading = ref(false)
const saveConfigLoading = ref(false)
const saveUserConfigLoading = ref(false)
const clearLogLoading = ref(false)
const clearAppLogLoading = ref(false)
const runLogRef = ref<{ clear: () => void } | null>(null)
const appLogRef = ref<{ clear: () => void } | null>(null)
const { data: postgresPassword } = useRequest(postgresql.postgresPassword, {
initialData: ''
})
const { data: log } = useRequest(postgresql.log, {
initialData: ''
const selectedLog = ref('')
const { data: logList } = useRequest(postgresql.log, {
initialData: []
}).onSuccess(({ data }) => {
if (!data?.length) {
selectedLog.value = ''
return
}
// log_filename='postgresql-%a.log' 按 weekday 缩写循环;优先选中今天对应的文件
const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
const today = `postgresql-${weekdays[new Date().getDay()]}.log`
selectedLog.value = data.find((p: string) => p.endsWith(today)) ?? data[data.length - 1]
})
const logOptions = computed(() =>
(logList.value as string[]).map((p) => ({
label: p.split('/').pop() ?? p,
value: p
}))
)
const { data: config, send: refreshConfig } = useRequest(postgresql.config, {
initialData: ''
})
@@ -82,8 +104,9 @@ const handleSaveUserConfig = () => {
const handleClearLog = () => {
clearLogLoading.value = true
useRequest(postgresql.clearLog())
useRequest(systemctl.clearLog('postgresql'))
.onSuccess(() => {
runLogRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
.onComplete(() => {
@@ -91,6 +114,19 @@ const handleClearLog = () => {
})
}
const handleClearAppLog = () => {
if (!selectedLog.value) return
clearAppLogLoading.value = true
useRequest(file.truncate(selectedLog.value))
.onSuccess(() => {
appLogRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
.onComplete(() => {
clearAppLogLoading.value = false
})
}
const handleSetPostgresPassword = () => {
setPostgresPasswordLoading.value = true
useRequest(postgresql.setPostgresPassword(postgresPassword.value))
@@ -197,11 +233,19 @@ const handleCopyPostgresPassword = () => {
{{ $gettext('Clear Log') }}
</n-button>
</n-flex>
<realtime-log service="postgresql" />
<realtime-log ref="runLogRef" service="postgresql" />
</n-flex>
</n-tab-pane>
<n-tab-pane name="slow-log" :tab="$gettext('Slow Logs')">
<realtime-log :path="log" />
<n-tab-pane name="app-log" :tab="$gettext('Application Logs')">
<n-flex vertical>
<n-flex>
<n-select v-model:value="selectedLog" :options="logOptions" style="width: 240px" />
<n-button type="primary" :loading="clearAppLogLoading" :disabled="clearAppLogLoading || !selectedLog" @click="handleClearAppLog">
{{ $gettext('Clear Log') }}
</n-button>
</n-flex>
<realtime-log ref="appLogRef" :path="selectedLog" />
</n-flex>
</n-tab-pane>
</n-tabs>
</common-page>
+21 -3
View File
@@ -7,6 +7,7 @@ import { NButton, NDataTable, NInput, NPopconfirm } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import supervisor from '@/api/apps/supervisor'
import file from '@/api/panel/file'
import ServiceStatus from '@/components/common/ServiceStatus.vue'
const { $gettext } = useGettext()
@@ -16,6 +17,9 @@ const saveProcessConfigLoading = ref(false)
const clearLogLoading = ref(false)
const createProcessLoading = ref(false)
const processLog = ref('')
const processLogModalRef = ref<{ clear: () => void } | null>(null)
const daemonLogRef = ref<{ clear: () => void } | null>(null)
const daemonLogPath = '/var/log/supervisor/supervisord.log'
const { data: serviceName } = useRequest(supervisor.service, {
initialData: ''
@@ -231,8 +235,9 @@ const handleSaveConfig = () => {
const handleClearLog = () => {
clearLogLoading.value = true
useRequest(supervisor.clearLog())
useRequest(file.truncate(daemonLogPath))
.onSuccess(() => {
daemonLogRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
.onComplete(() => {
@@ -286,6 +291,13 @@ const handleShowProcessLog = async (row: any) => {
processLogModal.value = true
}
const handleClearProcessLog = () => {
useRequest(file.truncate(processLog.value)).onSuccess(() => {
processLogModalRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
}
const handleEditProcess = async (name: string) => {
await getProcessConfig(name)
editProcessModal.value = true
@@ -380,7 +392,7 @@ onUnmounted(() => {
{{ $gettext('Clear Log') }}
</n-button>
</n-flex>
<realtime-log path="/var/log/supervisor/supervisord.log" />
<realtime-log ref="daemonLogRef" :path="daemonLogPath" />
</n-flex>
</n-tab-pane>
</n-tabs>
@@ -434,7 +446,13 @@ onUnmounted(() => {
</n-form>
<n-button type="info" block :loading="createProcessLoading" :disabled="createProcessLoading" @click="handleCreateProcess">{{ $gettext('Submit') }}</n-button>
</n-modal>
<realtime-log-modal v-model:show="processLogModal" :path="processLog" />
<realtime-log-modal
ref="processLogModalRef"
v-model:show="processLogModal"
:path="processLog"
clearable
@clear="handleClearProcessLog"
/>
<n-modal
v-model:show="editProcessModal"
preset="card"
+10 -4
View File
@@ -5,6 +5,7 @@ import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import php from '@/api/panel/environment/php'
import file from '@/api/panel/file'
const route = useRoute()
const slug = Number(route.params.slug)
@@ -32,6 +33,9 @@ watch(currentTab, (val) => {
refreshFpmConfig()
}
})
const logRef = ref<{ clear: () => void } | null>(null)
const slowLogRef = ref<{ clear: () => void } | null>(null)
const { data: log } = useRequest(php.log(slug), {
initialData: ''
})
@@ -171,13 +175,15 @@ const handleSaveFPMConfig = async () => {
}
const handleClearLog = async () => {
useRequest(php.clearLog(slug)).onSuccess(() => {
useRequest(file.truncate(log.value)).onSuccess(() => {
logRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
}
const handleClearSlowLog = async () => {
useRequest(php.clearSlowLog(slug)).onSuccess(() => {
useRequest(file.truncate(slowLog.value)).onSuccess(() => {
slowLogRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
}
@@ -290,7 +296,7 @@ const handleUninstallModule = async (module: string) => {
{{ $gettext('Clear Log') }}
</n-button>
</n-flex>
<realtime-log :path="log" />
<realtime-log ref="logRef" :path="log" />
</n-flex>
</n-tab-pane>
<n-tab-pane name="slow-log" :tab="$gettext('Slow Logs')">
@@ -300,7 +306,7 @@ const handleUninstallModule = async (module: string) => {
{{ $gettext('Clear Slow Log') }}
</n-button>
</n-flex>
<realtime-log :path="slowLog" />
<realtime-log ref="slowLogRef" :path="slowLog" />
</n-flex>
</n-tab-pane>
</n-tabs>
+16 -1
View File
@@ -12,6 +12,15 @@ import CreateModal from '@/views/task/CreateModal.vue'
const { $gettext } = useGettext()
const logPath = ref('')
const logModal = ref(false)
const logModalRef = ref<{ clear: () => void } | null>(null)
const handleClearLog = () => {
if (!logPath.value) return
useRequest(file.truncate(logPath.value)).onSuccess(() => {
logModalRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
}
const shellEditModal = ref(false)
const visualEditModal = ref(false)
const saveTaskEditLoading = ref(false)
@@ -305,7 +314,13 @@ onUnmounted(() => {
pageSizes: [20, 50, 100, 200]
}"
/>
<realtime-log-modal v-model:show="logModal" :path="logPath" />
<realtime-log-modal
ref="logModalRef"
v-model:show="logModal"
:path="logPath"
clearable
@clear="handleClearLog"
/>
<!-- Shell 脚本编辑模态框 -->
<n-modal
v-model:show="shellEditModal"
+17 -1
View File
@@ -2,6 +2,7 @@
import { NButton, NDataTable, NPopconfirm } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import file from '@/api/panel/file'
import task from '@/api/panel/task'
import RealtimeLogModal from '@/components/common/RealtimeLogModal.vue'
import { formatDateTime } from '@/utils'
@@ -9,6 +10,15 @@ import { formatDateTime } from '@/utils'
const { $gettext } = useGettext()
const logModal = ref(false)
const logPath = ref('')
const logModalRef = ref<{ clear: () => void } | null>(null)
const handleClearLog = () => {
if (!logPath.value) return
useRequest(file.truncate(logPath.value)).onSuccess(() => {
logModalRef.value?.clear()
window.$message.success($gettext('Cleared successfully'))
})
}
const columns: any = [
{
@@ -154,5 +164,11 @@ onMounted(() => {
}"
/>
</n-flex>
<realtime-log-modal v-model:show="logModal" :path="logPath" />
<realtime-log-modal
ref="logModalRef"
v-model:show="logModal"
:path="logPath"
clearable
@clear="handleClearLog"
/>
</template>
+5 -3
View File
@@ -5,6 +5,7 @@ import { useGettext } from 'vue3-gettext'
import draggable from 'vuedraggable'
import cert from '@/api/panel/cert'
import file from '@/api/panel/file'
import home from '@/api/panel/home'
import website from '@/api/panel/website'
import KeyValueEditor from '@/components/common/KeyValueEditor.vue'
@@ -246,10 +247,11 @@ const handleSelectCert = (value: number) => {
}
const clearLog = async () => {
const path = current.value === 'error_log' ? setting.value.error_log : setting.value.access_log
if (!path || path === 'off') return
clearLogLoading.value = true
useRequest(website.clearLog(id.value))
useRequest(file.truncate(path))
.onSuccess(() => {
fetchSetting()
window.$message.success($gettext('Cleared successfully'))
})
.onComplete(() => {
@@ -2030,7 +2032,7 @@ const removeCustomConfig = (index: number) => {
</n-spin>
<template #footer>
<n-flex justify="end">
<n-popconfirm v-if="current == 'log'" @positive-click="clearLog">
<n-popconfirm v-if="current === 'log' || current === 'error_log'" @positive-click="clearLog">
<template #trigger>
<n-button type="primary" :loading="clearLogLoading" :disabled="clearLogLoading">
{{ $gettext('Clear Logs') }}