Explorar el Código

Add 添加定时任务的文件日志功能

YueYunyun hace 2 años
padre
commit
7e90081ece

+ 30 - 0
SERVER/VberAdmin/app/schedule/jobs/clean-log.go

@@ -0,0 +1,30 @@
+package jobs
+
+import (
+	"VberAdmin/common"
+)
+
+// CleanLog
+// 新添加的job 必须按照以下格式定义,并实现Exec函数
+type CleanLog struct {
+}
+
+func (t *CleanLog) Exec(arg interface{}) error {
+	jobLogger.Infof("【CleanLog】 EXEC START")
+	err := common.CleanSysLog()
+	if err != nil {
+		jobLogger.Errorf("[CleanSysLog] EXEC ERROR: %s", err.Error())
+		return err
+	}
+	err = CleanJobLog(10)
+	if err != nil {
+		jobLogger.Errorf("[CleanSysLog] EXEC ERROR: %s", err.Error())
+		return err
+	}
+	jobLogger.Infof("【CleanLog】 EXEC END\r\n")
+	return nil
+}
+
+func (t *CleanLog) GetName() string {
+	return "清理日志"
+}

+ 0 - 30
SERVER/VberAdmin/app/schedule/jobs/examples.go

@@ -1,30 +0,0 @@
-package jobs
-
-import (
-	"fmt"
-	"time"
-)
-
-// ExamplesOne
-// 新添加的job 必须按照以下格式定义,并实现Exec函数
-type ExamplesOne struct {
-}
-
-func (t *ExamplesOne) Exec(arg interface{}) error {
-	str := time.Now().Format(timeFormat) + " [INFO] JobCore ExamplesOne exec success"
-	// TODO: 这里需要注意 Examples 传入参数是 string 所以 arg.(string);请根据对应的类型进行转化;
-	switch arg.(type) {
-
-	case string:
-		if arg.(string) != "" {
-			fmt.Println("string", arg.(string))
-			fmt.Println(str, arg.(string))
-		} else {
-			fmt.Println("arg is nil")
-			fmt.Println(str, "arg is nil")
-		}
-		break
-	}
-
-	return nil
-}

+ 12 - 16
SERVER/VberAdmin/app/schedule/jobs/init.go

@@ -4,8 +4,6 @@ import (
 	"VberAdmin/app/schedule/models"
 	"VberAdmin/core/sdk"
 	"VberAdmin/core/sdk/pkg/cronjob"
-	"fmt"
-	"time"
 
 	"gorm.io/gorm"
 )
@@ -25,18 +23,16 @@ func InitJob() {
 // 字典 key 可以配置到 自动任务 调用目标 中;
 func initJobList() {
 	jobList = map[string]JobExec{
-		"ExamplesOne": &ExamplesOne{},
+		"CleanLog": &CleanLog{},
 		// ...
 	}
 }
 
 // Setup 初始化
 func Setup(dbs map[string]*gorm.DB) {
-
-	fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore Starting...")
-
+	jobLogger.Info("STARTING...")
 	for k, db := range dbs {
-		sdk.Runtime.SetCrontab(k, cronjob.NewWithSeconds())
+		sdk.Runtime.SetCrontab(k, cronjob.NewWithSecondsLogger(jobLogger))
 		setup(k, db)
 	}
 }
@@ -47,15 +43,15 @@ func setup(key string, db *gorm.DB) {
 	jobList := make([]models.SysJob, 0)
 	err := sysJob.GetList(db, &jobList)
 	if err != nil {
-		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore init error", err)
+		jobLogger.Errorf("INIT ERROR: %s", err.Error())
 	}
 	if len(jobList) == 0 {
-		fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore total:0")
+		jobLogger.Infof("TOTAL:0")
 	}
 
 	_, err = sysJob.RemoveAllEntryID(db)
 	if err != nil {
-		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore remove entry_id error", err)
+		jobLogger.Errorf("REMOVE ALL ENTRY ERROR: %s", err.Error())
 	}
 
 	for i := 0; i < len(jobList); i++ {
@@ -81,17 +77,17 @@ func setup(key string, db *gorm.DB) {
 
 	// 其中任务
 	crontab.Start()
-	fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore start success.")
+	jobLogger.Infof("START SUCCESS.")
 	// 关闭任务
 	defer crontab.Stop()
 	select {}
 }
 
 // GetJobKeys 获取job key列表
-func GetJobKeys() []string {
-	var list []string
-	for k := range jobList {
-		list = append(list, k)
+func GetJobKeys() map[string]string {
+	var mp = make(map[string]string)
+	for k, v := range jobList {
+		mp[k] = v.GetName()
 	}
-	return list
+	return mp
 }

+ 3 - 4
SERVER/VberAdmin/app/schedule/jobs/job_http.go

@@ -1,7 +1,6 @@
 package jobs
 
 import (
-	log "VberAdmin/core/logger"
 	"VberAdmin/core/sdk/pkg"
 	"fmt"
 	"time"
@@ -28,8 +27,8 @@ LOOP:
 		str, err = pkg.Get(h.InvokeTarget)
 		if err != nil {
 			// 如果失败暂停一段时间重试
-			fmt.Println(time.Now().Format(timeFormat), " [ERROR] mission failed! ", err)
-			fmt.Printf(time.Now().Format(timeFormat)+" [INFO] Retry after the task fails %d seconds! %s \n", (count+1)*5, str)
+			jobLogger.Errorf("[%s] EXEC FAILED! %s", h.Name, err.Error())
+			jobLogger.Infof("[%s]  Retry after the task fails %d seconds! %s ", h.Name, (count+1)*5, str)
 			time.Sleep(time.Duration(count+1) * 5 * time.Second)
 			count = count + 1
 			goto LOOP
@@ -42,7 +41,7 @@ LOOP:
 	latencyTime := endTime.Sub(startTime)
 	//TODO: 待完善部分
 
-	log.Info("[Job] JobCore %s exec success , spend :%v", h.Name, latencyTime)
+	jobLogger.Infof("%s EXEC SUCCESS , SPEND :%v", h.Name, latencyTime)
 	return
 }
 

+ 3 - 4
SERVER/VberAdmin/app/schedule/jobs/job_sys.go

@@ -1,7 +1,6 @@
 package jobs
 
 import (
-	log "VberAdmin/core/logger"
 	"fmt"
 	"time"
 
@@ -16,7 +15,7 @@ func (e *ExecJob) Run() {
 	startTime := time.Now()
 	var obj = jobList[e.InvokeTarget]
 	if obj == nil {
-		log.Warn("[Job] ExecJob Run jobs nil")
+		jobLogger.Warnf("[Job] ExecJob Run jobs nil [%s]", e.Name)
 		return
 	}
 	err := CallExec(obj.(JobExec), e.Args)
@@ -32,14 +31,14 @@ func (e *ExecJob) Run() {
 	//TODO: 待完善部分
 	//str := time.Now().Format(timeFormat) + " [INFO] JobCore " + string(e.EntryId) + "exec success , spend :" + latencyTime.String()
 	//ws.SendAll(str)
-	log.Infof("[Job] JobCore %s exec success , spend :%v", e.Name, latencyTime)
+	jobLogger.Infof("[%s] EXEC SUCCESS , SPEND :%v", e.Name, latencyTime)
 	return
 }
 
 func (e *ExecJob) addJob(c *cron.Cron) (int, error) {
 	id, err := c.AddJob(e.CronExpression, e)
 	if err != nil {
-		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore AddJob error", err)
+		jobLogger.Errorf("AddJob ERROR: %s", err.Error())
 		return 0, err
 	}
 	EntryId := int(id)

+ 2 - 5
SERVER/VberAdmin/app/schedule/jobs/jobbase.go

@@ -1,9 +1,6 @@
 package jobs
 
 import (
-	"fmt"
-	"time"
-
 	"github.com/robfig/cron/v3"
 )
 
@@ -13,7 +10,7 @@ var retryCount = 3
 // AddJob 添加任务 AddJob(invokeTarget string, jobId int, jobName string, cronExpression string)
 func AddJob(c *cron.Cron, job Job) (int, error) {
 	if job == nil {
-		fmt.Println("unknown")
+		jobLogger.Errorf("Job Is Nil")
 		return 0, nil
 	}
 	return job.addJob(c)
@@ -24,7 +21,7 @@ func Remove(c *cron.Cron, entryID int) chan bool {
 	ch := make(chan bool)
 	go func() {
 		c.Remove(cron.EntryID(entryID))
-		fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore Remove success ,info entryID :", entryID)
+		jobLogger.Infof("Remove SUCCESS ,INFO entryID :%d", entryID)
 		ch <- true
 	}()
 	return ch

+ 99 - 0
SERVER/VberAdmin/app/schedule/jobs/logger.go

@@ -0,0 +1,99 @@
+package jobs
+
+import (
+	"VberAdmin/core/logger"
+	"VberAdmin/core/sdk/config"
+	cLog "VberAdmin/core/sdk/pkg/logger"
+	"VberAdmin/core/tools/writer"
+	"fmt"
+	"log"
+	"sync"
+)
+
+var jobLogger *JobLogger
+var once sync.Once
+
+type JobLogger struct {
+	*logger.Helper
+}
+
+func init() {
+	GetJobLogger()
+}
+
+func GetJobLogger() *JobLogger {
+	once.Do(func() {
+		loggerHelper := setupJobLogger()
+		jobLogger = &JobLogger{
+			loggerHelper,
+		}
+	})
+	return jobLogger
+}
+
+// setupJobLogger 设置设备日志组件
+func setupJobLogger() *logger.Helper {
+	path := getJobLogPath()
+	logCap := config.LoggerConfig.Cap
+	if logCap == 0 {
+		logCap = 1024
+	}
+	output, err := writer.NewFileWriter(
+		writer.WithPath(path),
+		writer.WithCap(logCap<<10),
+		writer.WithSuffix("log"),
+	)
+	if err != nil {
+		log.Printf("job logger setup error: %s \r\n", err.Error())
+	}
+	level := logger.DebugLevel
+	if config.ApplicationConfig.Mode == "prod" {
+		level = logger.InfoLevel
+	}
+	return logger.NewHelper(logger.NewLogger(logger.WithLevel(level), logger.WithOutput(output), logger.WithName("JOB")))
+}
+
+func getJobLogPath() string {
+	path := config.LoggerConfig.Path
+	if path == "" {
+		path = "_logs/"
+	} else if path[len(path)-1] != '/' {
+		path += "/"
+	}
+	path += "jobs"
+	return path
+}
+
+func CleanJobLog(days int) error {
+	path := getJobLogPath()
+	return cLog.DeleteLogsOlderThan(path, "log", days)
+}
+
+func (j *JobLogger) Info(msg string, args ...interface{}) {
+	msg = "[CRON]" + msg
+	if len(args) > 0 {
+		for _, v := range args {
+			msg += fmt.Sprintf(" %v", v)
+		}
+	}
+	j.Helper.Info(msg)
+}
+func (j *JobLogger) Infof(msg string, args ...interface{}) {
+	msg = "[JobCore]" + msg
+	j.Helper.Infof(msg, args...)
+}
+
+func (j *JobLogger) Error(err error, msg string, args ...interface{}) {
+	msg = "[CRON]" + msg
+	if len(args) > 0 {
+		for _, v := range args {
+			msg += fmt.Sprintf("%v ", v)
+		}
+	}
+	j.Helper.Errorf("%s; ERROR: %s", msg, err.Error())
+}
+
+func (j *JobLogger) Errorf(msg string, args ...interface{}) {
+	msg = "[JobCore]" + msg
+	j.Helper.Errorf(msg, args...)
+}

+ 1 - 0
SERVER/VberAdmin/app/schedule/jobs/type.go

@@ -18,6 +18,7 @@ type Job interface {
 
 type JobExec interface {
 	Exec(arg interface{}) error
+	GetName() string
 }
 
 func CallExec(e JobExec, arg interface{}) error {

+ 16 - 0
SERVER/VberAdmin/common/clean.go

@@ -0,0 +1,16 @@
+package common
+
+import (
+	"VberAdmin/core/sdk/config"
+	"VberAdmin/core/sdk/pkg/logger"
+)
+
+// CleanSysLog 清理系统日志文件
+func CleanSysLog() error {
+	days := config.LoggerConfig.SaveDays
+	if days <= 0 {
+		return nil
+	}
+	path := config.LoggerConfig.Path
+	return logger.DeleteLogsOlderThan(path, "log", days)
+}

+ 2 - 3
SERVER/VberAdmin/config/sql/db.sql

@@ -374,9 +374,8 @@ INSERT INTO sys_dict_data VALUES (42, 0, '清空数据', '12', 'sys_oper_type',
 INSERT INTO sys_dict_data VALUES (43, 0, '刷新数据', '13', 'sys_oper_type', '', 'primary', '', '2', '', '获取验证码', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
 
 
-
-INSERT INTO sys_job VALUES (1, '接口测试', 'SYSTEM', 1, '0/5 * * * * ', 'http://localhost:6071', '', 1, 1, 1, 0, 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
-INSERT INTO sys_job VALUES (2, '函数测试', 'SYSTEM', 2, '0/5 * * * * ', 'ExamplesOne', '参数', 1, 1, 1, 0, 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
+INSERT INTO sys_job VALUES (1, '清理日志', 'SYSTEM', 2, '0/5 * * * * ', 'CleanLog', '', 1, 1, 2, 0, 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
+INSERT INTO sys_job VALUES (2, '接口测试', 'SYSTEM', 1, '0/5 * * * * ', 'http://localhost:6071', '', 1, 1, 1, 0, 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
 
 
 INSERT INTO sys_post VALUES (1, '首席执行官', 'CEO', 0, '2','首席执行官', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);

+ 10 - 25
SERVER/VberAdmin/core/logger/default.go

@@ -87,32 +87,31 @@ func (l *defaultLogger) logf(level Level, format string, v ...interface{}) {
 	l.RLock()
 	fields := copyFields(l.opts.Fields)
 	l.RUnlock()
-
-	fields["level"] = level.String()
-
-	if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok && level.String() == "error" {
-		fields["file"] = fmt.Sprintf("%s:%d", logCallerFilePath(file), line)
-	}
-
 	rec := dlog.Record{
 		Timestamp: time.Now(),
 		Metadata:  make(map[string]string, len(fields)),
 	}
+	//s := "[" + rec.Timestamp.Format("2006-01-02 15:04:05.000Z0700")
+	str := "[" + rec.Timestamp.Format("2006-01-02 15:04:05.000")
+	if l.opts.Name != "" {
+		str += " " + l.opts.Name
+	}
+	str += "][" + strings.ToUpper(level.String()) + "]"
+	if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok && level.String() == "error" {
+		str += "[ERR:" + fmt.Sprintf("%s:%d", logCallerFilePath(file), line) + "]"
+	}
 	if format == "" {
 		rec.Message = fmt.Sprint(v...)
 	} else {
 		rec.Message = fmt.Sprintf(format, v...)
 	}
-
 	keys := make([]string, 0, len(fields))
 	for k, v := range fields {
 		keys = append(keys, k)
 		rec.Metadata[k] = fmt.Sprintf("%v", v)
 	}
-
 	sort.Strings(keys)
 	metadata := ""
-
 	for i, k := range keys {
 		if i == 0 {
 			metadata += fmt.Sprintf("%v", fields[k])
@@ -121,21 +120,7 @@ func (l *defaultLogger) logf(level Level, format string, v ...interface{}) {
 		}
 	}
 
-	var name string
-	if l.opts.Name != "" {
-		name = "[" + l.opts.Name + "]"
-	}
-	t := rec.Timestamp.Format("2006-01-02 15:04:05.000Z0700")
-	//fmt.Printf("%s\n", t)
-	//fmt.Printf("%s\n", name)
-	//fmt.Printf("%s\n", metadata)
-	//fmt.Printf("%v\n", rec.Message)
-	logStr := ""
-	if name == "" {
-		logStr = fmt.Sprintf("%s %s %v\n", t, metadata, rec.Message)
-	} else {
-		logStr = fmt.Sprintf("%s %s %s %v\n", name, t, metadata, rec.Message)
-	}
+	logStr := fmt.Sprintf("%s %s %v\n", str, metadata, rec.Message)
 	_, err := l.opts.Out.Write([]byte(logStr))
 	if err != nil {
 		log.Printf("log [Logf] write error: %s \n", err.Error())

+ 1 - 0
SERVER/VberAdmin/core/sdk/config/logger.go

@@ -13,6 +13,7 @@ type Logger struct {
 	Stdout    string
 	EnabledDB bool
 	Cap       uint
+	SaveDays  int
 }
 
 // Setup 设置logger

+ 5 - 0
SERVER/VberAdmin/core/sdk/pkg/cronjob/gadmjob.go

@@ -10,3 +10,8 @@ func NewWithSeconds() *cron.Cron {
 		cron.Hour | cron.Dom | cron.Month | cron.DowOptional | cron.Descriptor)
 	return cron.New(cron.WithParser(secondParser), cron.WithChain())
 }
+func NewWithSecondsLogger(logger cron.Logger) *cron.Cron {
+	secondParser := cron.NewParser(cron.Second | cron.Minute |
+		cron.Hour | cron.Dom | cron.Month | cron.DowOptional | cron.Descriptor)
+	return cron.New(cron.WithParser(secondParser), cron.WithChain(), cron.WithLogger(logger))
+}

+ 92 - 0
SERVER/VberAdmin/core/sdk/pkg/logger/clean.go

@@ -0,0 +1,92 @@
+package logger
+
+import (
+	"VberAdmin/core/logger"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+// DeleteLogsOlderThan 删除比指定日期早的日志文件。
+// days 参数表示需要删除的日志文件是几天前的。
+func DeleteLogsOlderThan(path, suffix string, days int) error {
+	threshold := time.Now().AddDate(0, 0, -days)
+
+	files, err := filepath.Glob(filepath.Join(path, "*."+suffix)) // 假设日志文件以.log结尾
+	if err != nil {
+		return fmt.Errorf("failed to list files in %s: %w", path, err)
+	}
+
+	for _, file := range files {
+		err2 := deleteFile(file, threshold)
+		if err2 != nil {
+			return err2
+		}
+	}
+
+	return nil
+}
+
+// DeleteAllLogsOlderThan 删除比指定日期早的日志文件(包括子目录下)。
+// days 参数表示需要删除的日志文件是几天前的。
+func DeleteAllLogsOlderThan(path, suffix string, days int) error {
+	threshold := time.Now().AddDate(0, 0, 0)
+
+	files, err := recursivelyFindFiles(path, suffix)
+	if err != nil {
+		return fmt.Errorf("failed to list files in %s: %w", path, err)
+	}
+
+	for _, file := range files {
+		err2 := deleteFile(file, threshold)
+		if err2 != nil {
+			return err2
+		}
+	}
+
+	return nil
+}
+
+func deleteFile(file string, threshold time.Time) error {
+	fileInfo, err := os.Stat(file)
+	if err != nil {
+		if os.IsNotExist(err) {
+			logger.Errorf("File %s does not exist\n", file)
+		} else {
+			return fmt.Errorf("failed to get info for %s: %w", file, err)
+		}
+	}
+
+	// 检查文件修改时间是否早于阈值
+	if fileInfo.ModTime().Before(threshold) {
+		err = os.Remove(file)
+		if err != nil {
+			return fmt.Errorf("failed to delete %s: %w", file, err)
+		}
+		logger.Infof("删除日志文件: %s\n", file)
+	}
+	return nil
+}
+
+// recursivelyFindFiles 递归地查找指定目录下的所有文件
+func recursivelyFindFiles(dirPath string, suffix string) ([]string, error) {
+	var files []string
+
+	err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+		if !info.IsDir() && strings.HasSuffix(info.Name(), suffix) {
+			files = append(files, path)
+		}
+		return nil
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	return files, nil
+}

+ 4 - 0
UI/VAG.VUE/src/assets/sass/_common.scss

@@ -59,3 +59,7 @@ dl {
 		}
 	}
 }
+
+.el-button.btn {
+	display: inline-flex !important;
+}

+ 6 - 3
UI/VAG.VUE/src/views/schedule/job/index.vue

@@ -307,9 +307,12 @@ function handleStop(row: any) {
 
 function init() {
 	apis.schedule.jobApi.getJobKeys().then((res: any) => {
-		if (res.data && res.data.length > 0) {
-			jobKeysOptions.value = res.data.map((v) => {
-				return { label: v, value: v }
+		if (res.data) {
+			jobKeysOptions.value = Object.keys(res.data).map((key: any) => {
+				return {
+					value: key,
+					label: res.data[key]
+				}
 			})
 		}
 	})