aa9f892a32
- 新增数据库配置项(DB_TYPE, DB_HOST, DB_PORT等),支持MySQL和PostgreSQL - 集成GORM实现数据库连接和自动迁移功能 - 添加定时任务调度器(cmd/scheduler),基于robfig/cron实现秒级调度 - 实现事件Hook体系,支持同步/异步处理和优先级排序 - 更新构建脚本,编译server、cli、scheduler三个二进制文件 - 配置systemd服务管理定时任务调度器 - 重构项目结构,新增crontab和hooks目录模块 - 更新README文档,完善各组件使用说明和部署配置
231 lines
5.4 KiB
Go
231 lines
5.4 KiB
Go
package logger
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
reset = "\033[0m"
|
|
red = "\033[31m"
|
|
green = "\033[32m"
|
|
yellow = "\033[33m"
|
|
blue = "\033[34m"
|
|
cyan = "\033[36m"
|
|
white = "\033[97m"
|
|
)
|
|
|
|
type colorFormatter struct{}
|
|
|
|
func (f *colorFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
|
var levelColor string
|
|
switch entry.Level {
|
|
case logrus.DebugLevel:
|
|
levelColor = cyan
|
|
case logrus.InfoLevel:
|
|
levelColor = green
|
|
case logrus.WarnLevel:
|
|
levelColor = yellow
|
|
case logrus.ErrorLevel:
|
|
levelColor = red
|
|
case logrus.FatalLevel, logrus.PanicLevel:
|
|
levelColor = red
|
|
default:
|
|
levelColor = white
|
|
}
|
|
|
|
timestamp := fmt.Sprintf("%s%s%s", levelColor, entry.Time.Format("01-02 15:04:05"), reset)
|
|
|
|
file := "unknown"
|
|
line := 0
|
|
if entry.Caller != nil {
|
|
file = entry.Caller.File
|
|
if idx := strings.LastIndex(file, "/"); idx >= 0 {
|
|
file = file[idx+1:]
|
|
}
|
|
line = entry.Caller.Line
|
|
}
|
|
|
|
title, _ := entry.Data["title"].(string)
|
|
if title == "" {
|
|
title = "-"
|
|
}
|
|
|
|
level := fmt.Sprintf("%s[%s]%s", levelColor, strings.ToUpper(entry.Level.String()), reset)
|
|
titleStr := fmt.Sprintf("%s「%s」%s", levelColor, title, reset)
|
|
|
|
msg := fmt.Sprintf("%s %s:%d: %s >> %s %s\n",
|
|
timestamp, file, line, level, titleStr, entry.Message,
|
|
)
|
|
return []byte(msg), nil
|
|
}
|
|
|
|
var (
|
|
instance *logrus.Logger
|
|
once sync.Once
|
|
cronInstance *logrus.Logger
|
|
cronOnce sync.Once
|
|
)
|
|
|
|
func GetLogger() *logrus.Logger {
|
|
once.Do(func() {
|
|
instance = logrus.New()
|
|
|
|
switch strings.ToLower(os.Getenv("LOG_LEVEL")) {
|
|
case "debug":
|
|
instance.SetLevel(logrus.DebugLevel)
|
|
case "info":
|
|
instance.SetLevel(logrus.InfoLevel)
|
|
case "warn":
|
|
instance.SetLevel(logrus.WarnLevel)
|
|
case "error":
|
|
instance.SetLevel(logrus.ErrorLevel)
|
|
case "fatal":
|
|
instance.SetLevel(logrus.FatalLevel)
|
|
default:
|
|
instance.SetLevel(logrus.InfoLevel)
|
|
}
|
|
|
|
instance.SetFormatter(&colorFormatter{})
|
|
instance.SetReportCaller(true)
|
|
|
|
if os.Getenv("LOG_SAVE") == "true" {
|
|
logDir := os.Getenv("LOG_SAVE_PATH")
|
|
if logDir != "" {
|
|
if err := os.MkdirAll(logDir, 0755); err != nil {
|
|
instance.Errorf("创建日志目录失败: %v", err)
|
|
} else {
|
|
logFile := logDir + "/" + time.Now().Format("20060102") + ".log"
|
|
f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
if err == nil {
|
|
instance.SetOutput(io.MultiWriter(os.Stdout, f))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
return instance
|
|
}
|
|
|
|
func callerSkip() *runtime.Frame {
|
|
pcs := make([]uintptr, 10)
|
|
n := runtime.Callers(3, pcs)
|
|
frames := runtime.CallersFrames(pcs[:n])
|
|
for {
|
|
frame, more := frames.Next()
|
|
if !strings.Contains(frame.File, "utils/logger/") {
|
|
return &frame
|
|
}
|
|
if !more {
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func toString(v interface{}) string {
|
|
rv := reflect.ValueOf(v)
|
|
if rv.Kind() == reflect.Struct || (rv.Kind() == reflect.Ptr && rv.Elem().Kind() == reflect.Struct) {
|
|
data, err := json.Marshal(v)
|
|
if err != nil {
|
|
return fmt.Sprintf("%v", v)
|
|
}
|
|
return string(data)
|
|
}
|
|
return fmt.Sprintf("%v", v)
|
|
}
|
|
|
|
func joinToString(parts ...interface{}) string {
|
|
strs := make([]string, 0, len(parts))
|
|
for _, p := range parts {
|
|
strs = append(strs, toString(p))
|
|
}
|
|
return strings.Join(strs, " ")
|
|
}
|
|
|
|
// GetCronLogger 返回定时任务专用 logger,写入独立日志文件
|
|
func GetCronLogger() *logrus.Logger {
|
|
cronOnce.Do(func() {
|
|
cronInstance = logrus.New()
|
|
|
|
switch strings.ToLower(os.Getenv("LOG_LEVEL")) {
|
|
case "debug":
|
|
cronInstance.SetLevel(logrus.DebugLevel)
|
|
case "info":
|
|
cronInstance.SetLevel(logrus.InfoLevel)
|
|
case "warn":
|
|
cronInstance.SetLevel(logrus.WarnLevel)
|
|
case "error":
|
|
cronInstance.SetLevel(logrus.ErrorLevel)
|
|
default:
|
|
cronInstance.SetLevel(logrus.InfoLevel)
|
|
}
|
|
|
|
cronInstance.SetFormatter(&colorFormatter{})
|
|
cronInstance.SetReportCaller(true)
|
|
|
|
logDir := os.Getenv("LOG_SAVE_PATH")
|
|
if logDir == "" {
|
|
logDir = "./logs"
|
|
}
|
|
if err := os.MkdirAll(logDir, 0755); err != nil {
|
|
cronInstance.SetOutput(os.Stdout)
|
|
return
|
|
}
|
|
|
|
logFile := logDir + "/cron_" + time.Now().Format("20060102") + ".log"
|
|
f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
cronInstance.SetOutput(os.Stdout)
|
|
return
|
|
}
|
|
cronInstance.SetOutput(io.MultiWriter(os.Stdout, f))
|
|
})
|
|
return cronInstance
|
|
}
|
|
|
|
func CronDebug(title string, content ...interface{}) {
|
|
GetCronLogger().WithField("title", title).Debug(joinToString(content...))
|
|
}
|
|
|
|
func CronInfo(title string, content ...interface{}) {
|
|
GetCronLogger().WithField("title", title).Info(joinToString(content...))
|
|
}
|
|
|
|
func CronWarn(title string, content ...interface{}) {
|
|
GetCronLogger().WithField("title", title).Warn(joinToString(content...))
|
|
}
|
|
|
|
func CronError(title string, content ...interface{}) {
|
|
GetCronLogger().WithField("title", title).Error(joinToString(content...))
|
|
}
|
|
|
|
func Debug(title string, content ...interface{}) {
|
|
GetLogger().WithField("title", title).Debug(joinToString(content...))
|
|
}
|
|
|
|
func Info(title string, content ...interface{}) {
|
|
GetLogger().WithField("title", title).Info(joinToString(content...))
|
|
}
|
|
|
|
func Warn(title string, content ...interface{}) {
|
|
GetLogger().WithField("title", title).Warn(joinToString(content...))
|
|
}
|
|
|
|
func Error(title string, content ...interface{}) {
|
|
GetLogger().WithField("title", title).Error(joinToString(content...))
|
|
}
|
|
|
|
func Fatal(title string, content ...interface{}) {
|
|
GetLogger().WithField("title", title).Fatal(joinToString(content...))
|
|
}
|