mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 16:29:17 +08:00
feat: 计划任务增加网站日志切割 (#1116)
Refs https://github.com/1Panel-dev/1Panel/issues/495
This commit is contained in:
parent
3e068a0020
commit
626782102a
@ -3,9 +3,11 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
@ -54,6 +56,11 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
}
|
||||
message = []byte(stdout)
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
case "cutWebsiteLog":
|
||||
record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("cut website log file failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
||||
@ -157,7 +164,7 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
if len(exclude) == 0 {
|
||||
continue
|
||||
}
|
||||
excludeRules += (" --exclude " + exclude)
|
||||
excludeRules += " --exclude " + exclude
|
||||
}
|
||||
path := ""
|
||||
if strings.Contains(sourceDir, "/") {
|
||||
@ -262,6 +269,72 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInf
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
||||
var (
|
||||
websites []string
|
||||
err error
|
||||
filePaths []string
|
||||
)
|
||||
if cronjob.Website == "all" {
|
||||
websites, _ = NewIWebsiteService().GetWebsiteOptions()
|
||||
if len(websites) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
websites = append(websites, cronjob.Website)
|
||||
}
|
||||
|
||||
nginx, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
baseDir := path.Join(nginx.GetPath(), "www", "sites")
|
||||
fileOp := files.NewFileOp()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(websites))
|
||||
for _, websiteName := range websites {
|
||||
name := websiteName
|
||||
go func() {
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithDomain(name))
|
||||
if website.ID == 0 {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
websiteLogDir := path.Join(baseDir, website.PrimaryDomain, "log")
|
||||
srcAccessLogPath := path.Join(websiteLogDir, "access.log")
|
||||
srcErrorLogPath := path.Join(websiteLogDir, "error.log")
|
||||
dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.PrimaryDomain)
|
||||
if !fileOp.Stat(dstLogDir) {
|
||||
_ = os.MkdirAll(dstLogDir, 0755)
|
||||
}
|
||||
|
||||
dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
|
||||
filePaths = append(filePaths, path.Join(dstLogDir, dstName))
|
||||
if err = fileOp.Compress([]string{srcAccessLogPath, srcErrorLogPath}, dstLogDir, dstName, files.Gz); err != nil {
|
||||
global.LOG.Errorf("There was an error in compressing the website[%s] access.log", website.PrimaryDomain)
|
||||
} else {
|
||||
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
|
||||
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
|
||||
}
|
||||
global.LOG.Infof("The website[%s] log file was successfully rotated in the directory [%s]", website.PrimaryDomain, dstLogDir)
|
||||
var record model.BackupRecord
|
||||
record.Type = "cutWebsiteLog"
|
||||
record.Name = cronjob.Website
|
||||
record.Source = "LOCAL"
|
||||
record.BackupType = "LOCAL"
|
||||
record.FileDir = dstLogDir
|
||||
record.FileName = dstName
|
||||
if err = backupRepo.CreateRecord(&record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
u.HandleRmExpired("LOCAL", "", cronjob, nil)
|
||||
return strings.Join(filePaths, ","), nil
|
||||
}
|
||||
|
||||
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
|
||||
var paths []string
|
||||
localDir, err := loadLocalDir()
|
||||
|
10
frontend/components.d.ts
vendored
10
frontend/components.d.ts
vendored
@ -5,7 +5,6 @@
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
404: typeof import('./src/components/error-message/404.vue')['default']
|
||||
AppLayout: typeof import('./src/components/app-layout/index.vue')['default']
|
||||
AppStatus: typeof import('./src/components/app-status/index.vue')['default']
|
||||
BackButton: typeof import('./src/components/back-button/index.vue')['default']
|
||||
Backup: typeof import('./src/components/backup/index.vue')['default']
|
||||
@ -13,13 +12,11 @@ declare module 'vue' {
|
||||
BreadCrumbsItem: typeof import('./src/components/bread-crumbs/bread-crumbs-item.vue')['default']
|
||||
CardWithHeader: typeof import('./src/components/card-with-header/index.vue')['default']
|
||||
Codemirror: typeof import('./src/components/codemirror-dialog/codemirror.vue')['default']
|
||||
Collapse: typeof import('./src/components/app-layout/menu/components/Collapse.vue')['default']
|
||||
ComplexTable: typeof import('./src/components/complex-table/index.vue')['default']
|
||||
ConfirmDialog: typeof import('./src/components/confirm-dialog/index.vue')['default']
|
||||
ContainerLog: typeof import('./src/components/container-log/index.vue')['default']
|
||||
DrawerHeader: typeof import('./src/components/drawer-header/index.vue')['default']
|
||||
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||
ElAside: typeof import('element-plus/es')['ElAside']
|
||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElBadge: typeof import('element-plus/es')['ElBadge']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
@ -29,7 +26,6 @@ declare module 'vue' {
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||
@ -40,7 +36,6 @@ declare module 'vue' {
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||
ElFooter: typeof import('element-plus/es')['ElFooter']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
@ -48,7 +43,6 @@ declare module 'vue' {
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||
ElLink: typeof import('element-plus/es')['ElLink']
|
||||
ElMain: typeof import('element-plus/es')['ElMain']
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
@ -76,20 +70,16 @@ declare module 'vue' {
|
||||
Err_ip: typeof import('./src/components/error-message/err_ip.vue')['default']
|
||||
FileList: typeof import('./src/components/file-list/index.vue')['default']
|
||||
FileRole: typeof import('./src/components/file-role/index.vue')['default']
|
||||
Footer: typeof import('./src/components/app-layout/footer/index.vue')['default']
|
||||
FormButton: typeof import('./src/components/layout-content/form-button.vue')['default']
|
||||
Group: typeof import('./src/components/group/index.vue')['default']
|
||||
InfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
|
||||
LayoutContent: typeof import('./src/components/layout-content/index.vue')['default']
|
||||
Line: typeof import('./src/components/v-charts/components/Line.vue')['default']
|
||||
Loading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
Logo: typeof import('./src/components/app-layout/menu/components/Logo.vue')['default']
|
||||
Menu: typeof import('./src/components/app-layout/menu/index.vue')['default']
|
||||
MsgInfo: typeof import('./src/components/msg-info/index.vue')['default']
|
||||
Popover: typeof import('element-plus/es')['ElPopoverDirective']
|
||||
RouterButton: typeof import('./src/components/router-button/index.vue')['default']
|
||||
Status: typeof import('./src/components/status/index.vue')['default']
|
||||
SubItem: typeof import('./src/components/app-layout/menu/components/sub-item.vue')['default']
|
||||
SvgIcon: typeof import('./src/components/svg-icon/svg-icon.vue')['default']
|
||||
SystemUpgrade: typeof import('./src/components/system-upgrade/index.vue')['default']
|
||||
TableSetting: typeof import('./src/components/table-setting/index.vue')['default']
|
||||
|
@ -656,6 +656,8 @@ const message = {
|
||||
cleanDataHelper: 'Delete the backup file generated during this task.',
|
||||
noLogs: 'No task output yet...',
|
||||
errPath: 'Backup path [{0}] error, cannot download!',
|
||||
cutWebsiteLog: 'cut website log',
|
||||
cutWebsiteLogHelper: 'The cut log files will be backed up to the backup directory of 1Panel',
|
||||
},
|
||||
monitor: {
|
||||
monitor: 'Monitor',
|
||||
|
@ -663,6 +663,8 @@ const message = {
|
||||
cleanDataHelper: '删除该任务执行过程中产生的备份文件',
|
||||
noLogs: '暂无任务输出...',
|
||||
errPath: '备份路径 [{0}] 错误,无法下载!',
|
||||
cutWebsiteLog: '切割网站日志',
|
||||
cutWebsiteLogHelper: '切割的日志文件会备份到 1Panel 的 backup 目录下',
|
||||
},
|
||||
monitor: {
|
||||
monitor: '监控',
|
||||
|
@ -18,6 +18,7 @@
|
||||
<el-option value="database" :label="$t('cronjob.database')" />
|
||||
<el-option value="directory" :label="$t('cronjob.directory')" />
|
||||
<el-option value="curl" :label="$t('cronjob.curl')" />
|
||||
<el-option value="cutWebsiteLog" :label="$t('cronjob.cutWebsiteLog')" />
|
||||
</el-select>
|
||||
<el-tag v-else>{{ dialogData.rowData!.type }}</el-tag>
|
||||
</el-form-item>
|
||||
@ -82,14 +83,17 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="dialogData.rowData!.type === 'website'"
|
||||
:label="$t('cronjob.website')"
|
||||
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'cutWebsiteLog'"
|
||||
:label="dialogData.rowData!.type === 'website' ? $t('cronjob.website'):$t('website.website')"
|
||||
prop="website"
|
||||
>
|
||||
<el-select style="width: 100%" v-model="dialogData.rowData!.website">
|
||||
<el-option :label="$t('commons.table.all')" value="all" />
|
||||
<el-option v-for="item in websiteOptions" :key="item" :value="item" :label="item" />
|
||||
</el-select>
|
||||
<span class="input-help" v-if="dialogData.rowData!.type === 'cutWebsiteLog'">
|
||||
{{ $t('cronjob.cutWebsiteLogHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="dialogData.rowData!.type === 'database'">
|
||||
|
Loading…
x
Reference in New Issue
Block a user