重构架构

This commit is contained in:
2026-02-20 18:44:43 +08:00
parent aba7b68439
commit cc71248ef4
52 changed files with 1404 additions and 2360 deletions

298
pkg/logger/logger.go Normal file
View File

@@ -0,0 +1,298 @@
// logger/logger.go
package logger
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/sirupsen/logrus"
)
// -------------------------- 1. ANSI 颜色码常量(关键) --------------------------
const (
// 颜色重置
ColorReset = "\033[0m"
// 灰色(日期、文件行号)
ColorGray = "\033[90m"
// 日志级别颜色
ColorDebug = "\033[36m" // 青色 [d]
ColorInfo = "\033[32m" // 绿色 [i]
ColorWarn = "\033[33m" // 黄色 [w]
ColorError = "\033[31m" // 红色 [e]
ColorFatal = "\033[35m" // 紫色 [f]
)
// -------------------------- 1. 定义日志接口 --------------------------
// Logger 日志核心接口,定义所有需要的日志方法
// 所有模块都依赖这个接口,而非具体实现
type Logger interface {
Debug(args ...interface{})
Debugf(format string, args ...interface{})
Info(args ...interface{})
Infof(format string, args ...interface{})
Warn(args ...interface{})
Warnf(format string, args ...interface{})
Error(args ...interface{})
Errorf(format string, args ...interface{})
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
}
// -------------------------- 2. 基于logrus的默认实现 --------------------------
// logrusLogger 是 Logger 接口的具体实现基于logrus
type logrusLogger struct {
*logrus.Logger
}
// 实现 Logger 接口的所有方法直接转发给logrus
func (l *logrusLogger) Debug(args ...interface{}) { l.Logger.Debug(args...) }
func (l *logrusLogger) Debugf(format string, args ...interface{}) { l.Logger.Debugf(format, args...) }
func (l *logrusLogger) Info(args ...interface{}) { l.Logger.Info(args...) }
func (l *logrusLogger) Infof(format string, args ...interface{}) { l.Logger.Infof(format, args...) }
func (l *logrusLogger) Warn(args ...interface{}) { l.Logger.Warn(args...) }
func (l *logrusLogger) Warnf(format string, args ...interface{}) { l.Logger.Warnf(format, args...) }
func (l *logrusLogger) Error(args ...interface{}) { l.Logger.Error(args...) }
func (l *logrusLogger) Errorf(format string, args ...interface{}) { l.Logger.Errorf(format, args...) }
func (l *logrusLogger) Fatal(args ...interface{}) { l.Logger.Fatal(args...) }
func (l *logrusLogger) Fatalf(format string, args ...interface{}) { l.Logger.Fatalf(format, args...) }
// -------------------------- 3. 全局默认实例 + 初始化逻辑 --------------------------
var (
// DefaultLogger 全局默认日志实例(所有模块可直接用,也可注入自定义实现)
DefaultLogger Logger
// once 保证日志只初始化一次
once sync.Once
)
// LogConfig 日志配置结构体和项目的config包对齐
type LogConfig struct {
Verbose bool // 是否开启详细模式Debug级别
Level string // 日志级别debug/info/warn/error
ShowColor bool // 是否显示彩色输出
LogFile string // 日志文件路径(可选,空则只输出到控制台)
}
// 默认配置
var defaultConfig = LogConfig{
Verbose: false,
Level: "info",
ShowColor: true,
LogFile: "/var/log/sunhpc/sunhpc.log",
}
// -------------------------- 5. 自定义Formatter核心配色逻辑 --------------------------
type CustomFormatter struct {
ShowColor bool // 是否显示彩色输出
}
// Format 实现 logrus.Formatter 接口,自定义日志格式和颜色
func (f *CustomFormatter) Format(entry *logrus.Entry) ([]byte, error) {
// 1. 获取日志级别标识和对应颜色
levelStr, levelColor := getLevelInfo(entry.Level)
// 2. 获取调用文件和行号(简化路径,只保留文件名+行号)
file, line := getCallerInfo()
// 3. 格式化时间(灰色)
timeStr := entry.Time.Format("2006-01-02 15:04:05")
// 构建日志行(按你的格式:日期 [级别] 内容 文件:行号)
var buf bytes.Buffer
// 颜色开关:如果禁用颜色,所有颜色码置空
colorReset := ColorReset
colorGray := ColorGray
if !f.ShowColor {
levelColor = ""
colorGray = ""
colorReset = ""
}
// 拼接格式:
// 灰色日期 + 空格 + 带颜色的[级别] + 空格 + 日志内容 + 空格 + 灰色文件行号 + 重置
fmt.Fprintf(&buf, "%s%s%s %s[%s]%s %s %s%s:%d%s\n",
colorGray, // 日期开始灰色
timeStr, // 日期字符串
colorReset, // 日期结束重置颜色
levelColor, // 级别标识颜色
levelStr, // 级别标识i/d/e/w/f
colorReset, // 级别标识结束重置
entry.Message, // 日志内容(默认色)
colorGray, // 文件行号开始灰色
file, // 文件名如db.go
line, // 行号如64
colorReset, // 文件行号结束重置
)
return buf.Bytes(), nil
}
// getLevelInfo 获取日志级别对应的标识和颜色
func getLevelInfo(level logrus.Level) (string, string) {
switch level {
case logrus.DebugLevel:
return "d", ColorDebug
case logrus.InfoLevel:
return "i", ColorInfo
case logrus.WarnLevel:
return "w", ColorWarn
case logrus.ErrorLevel:
return "e", ColorError
case logrus.FatalLevel:
return "f", ColorFatal
default:
return "i", ColorInfo
}
}
// getCallerInfo 获取调用日志的文件和行号跳过logrus内部调用
func _getCallerInfo() (string, int) {
// 跳过的调用栈深度根据实际情况调整这里跳过logrus和logger包的调用
skip := 6
pc, file, line, ok := runtime.Caller(skip)
if !ok {
return "unknown.go", 0
}
// 只保留文件名(如 db.go去掉完整路径
fileName := filepath.Base(file)
// 过滤logrus内部调用可选
funcName := runtime.FuncForPC(pc).Name()
if funcName == "" || filepath.Base(funcName) == "logrus" {
return getCallerInfoWithSkip(skip + 1)
}
return fileName, line
}
func getCallerInfo() (string, int) {
// 从当前调用开始,逐层向上查找
for i := 2; i < 15; i++ { // i从2开始跳过getCallerInfo自身
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
// 获取函数名
funcName := runtime.FuncForPC(pc).Name()
// 跳过logrus和logger包内部的调用
if shouldSkipPackage(funcName, file) {
continue
}
// 找到第一个非内部调用的栈帧
return filepath.Base(file), line
}
return "unknown.go", 0
}
// shouldSkipPackage 判断是否需要跳过该调用
func shouldSkipPackage(funcName, file string) bool {
// 跳过logrus包
if strings.Contains(funcName, "logrus") ||
strings.Contains(file, "logrus") {
return true
}
// 跳过logger包你自己的包装包
if strings.Contains(funcName, "your/package/logger") ||
strings.Contains(file, "logger") {
return true
}
// 跳过runtime包
if strings.Contains(funcName, "runtime.") {
return true
}
return false
}
// 递归调整调用栈深度(兼容不同场景)
func getCallerInfoWithSkip(skip int) (string, int) {
_, file, line, ok := runtime.Caller(skip)
if !ok {
return "unknown.go", 0
}
return filepath.Base(file), line
}
// Init 初始化全局默认日志实例(全局只执行一次)
func Init(cfg LogConfig) {
once.Do(func() {
// 合并配置:传入的配置为空则用默认值
if cfg.Level == "" {
cfg.Level = defaultConfig.Level
}
if cfg.LogFile == "" {
cfg.LogFile = defaultConfig.LogFile
}
// 1. 创建logrus实例
logrusInst := logrus.New()
// 2. 配置输出(控制台 + 文件,可选)
var outputs []io.Writer
outputs = append(outputs, os.Stdout) // 控制台输出
// 如果配置了日志文件,添加文件输出
if cfg.LogFile != "" {
// 确保日志目录存在
dir := filepath.Dir(cfg.LogFile)
if err := os.MkdirAll(dir, 0755); err != nil {
// 目录创建失败,只输出警告,不影响程序运行
logrusInst.Warnf("创建日志目录失败: %v,仅输出到控制台", err)
} else {
file, err := os.OpenFile(cfg.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err == nil {
outputs = append(outputs, file)
} else {
logrusInst.Warnf("打开日志文件失败: %v,仅输出到控制台", err)
}
}
}
logrusInst.SetOutput(io.MultiWriter(outputs...))
// 3. 配置格式
logrusInst.SetFormatter(&CustomFormatter{
ShowColor: cfg.ShowColor,
})
// 4. 配置日志级别
lvl, err := logrus.ParseLevel(cfg.Level)
if err != nil {
lvl = logrus.InfoLevel // 解析失败默认Info级别
}
// 开启Verbose则强制设为Debug级别
if cfg.Verbose {
lvl = logrus.DebugLevel
}
logrusInst.SetLevel(lvl)
// 启用文件行号(必须开启否则getCallerInfo拿不到数据)
logrusInst.SetReportCaller(true)
// 5. 赋值给全局默认实例
DefaultLogger = &logrusLogger{logrusInst}
})
}
// -------------------------- 4. 全局快捷调用方法(可选,简化使用) --------------------------
// 如果你不想每次都写 logger.DefaultLogger.Info(),可以封装快捷方法
func Debug(args ...any) { DefaultLogger.Debug(args...) }
func Debugf(format string, args ...any) { DefaultLogger.Debugf(format, args...) }
func Info(args ...any) { DefaultLogger.Info(args...) }
func Infof(format string, args ...any) { DefaultLogger.Infof(format, args...) }
func Warn(args ...any) { DefaultLogger.Warn(args...) }
func Warnf(format string, args ...any) { DefaultLogger.Warnf(format, args...) }
func Error(args ...any) { DefaultLogger.Error(args...) }
func Errorf(format string, args ...any) { DefaultLogger.Errorf(format, args...) }
func Fatal(args ...any) { DefaultLogger.Fatal(args...) }
func Fatalf(format string, args ...any) { DefaultLogger.Fatalf(format, args...) }