1
0
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:
zhengkunwang223 2023-05-23 18:54:05 +08:00 committed by GitHub
parent 3e068a0020
commit 626782102a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 13 deletions

View File

@ -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()

View File

@ -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']

View File

@ -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',

View File

@ -663,6 +663,8 @@ const message = {
cleanDataHelper: '删除该任务执行过程中产生的备份文件',
noLogs: '暂无任务输出...',
errPath: '备份路径 [{0}] 错误无法下载',
cutWebsiteLog: '切割网站日志',
cutWebsiteLogHelper: '切割的日志文件会备份到 1Panel backup 目录下',
},
monitor: {
monitor: '监控',

View File

@ -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'">