Files
apiServer-service/utils/logger/log.go
T
shiran aa9f892a32 feat: 添加数据库集成、定时任务调度器和事件Hook体系
- 新增数据库配置项(DB_TYPE, DB_HOST, DB_PORT等),支持MySQL和PostgreSQL
- 集成GORM实现数据库连接和自动迁移功能
- 添加定时任务调度器(cmd/scheduler),基于robfig/cron实现秒级调度
- 实现事件Hook体系,支持同步/异步处理和优先级排序
- 更新构建脚本,编译server、cli、scheduler三个二进制文件
- 配置systemd服务管理定时任务调度器
- 重构项目结构,新增crontab和hooks目录模块
- 更新README文档,完善各组件使用说明和部署配置
2026-04-15 12:39:59 +08:00

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...))
}