2026-02-20 18:44:43 +08:00
|
|
|
|
// 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 保证日志只初始化一次
|
2026-02-23 18:54:54 +08:00
|
|
|
|
initOnce sync.Once
|
2026-02-20 18:44:43 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// LogConfig 日志配置结构体(和项目的config包对齐)
|
|
|
|
|
|
type LogConfig struct {
|
2026-02-23 18:54:54 +08:00
|
|
|
|
Level string `mapstructure:"level" yaml:"level"`
|
|
|
|
|
|
Format string `mapstructure:"format" yaml:"format"`
|
|
|
|
|
|
Output string `mapstructure:"output" yaml:"output"`
|
|
|
|
|
|
Verbose bool `mapstructure:"verbose" yaml:"verbose"`
|
|
|
|
|
|
LogFile string `mapstructure:"log_file" yaml:"log_file"`
|
|
|
|
|
|
ShowColor bool `mapstructure:"show_color" yaml:"show_color"`
|
2026-02-20 18:44:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 默认配置
|
|
|
|
|
|
var defaultConfig = LogConfig{
|
|
|
|
|
|
Level: "info",
|
2026-02-23 18:54:54 +08:00
|
|
|
|
Format: "text",
|
|
|
|
|
|
Output: "stdout",
|
|
|
|
|
|
Verbose: false,
|
2026-02-20 18:44:43 +08:00
|
|
|
|
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 = ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-23 18:54:54 +08:00
|
|
|
|
// Debug,打印原始字节,用于调试
|
|
|
|
|
|
// fmt.Printf("%q\n", entry.Message)
|
|
|
|
|
|
|
2026-02-20 18:44:43 +08:00
|
|
|
|
// 拼接格式:
|
|
|
|
|
|
// 灰色日期 + 空格 + 带颜色的[级别] + 空格 + 日志内容 + 空格 + 灰色文件行号 + 重置
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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包(你自己的包装包)
|
2026-02-23 18:54:54 +08:00
|
|
|
|
if strings.Contains(file, "/logger/") {
|
2026-02-20 18:44:43 +08:00
|
|
|
|
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) {
|
2026-02-23 18:54:54 +08:00
|
|
|
|
initOnce.Do(func() {
|
2026-02-20 18:44:43 +08:00
|
|
|
|
// 1. 创建logrus实例
|
|
|
|
|
|
logrusInst := logrus.New()
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 配置输出(控制台 + 文件,可选)
|
|
|
|
|
|
var outputs []io.Writer
|
|
|
|
|
|
outputs = append(outputs, os.Stdout) // 控制台输出
|
2026-02-23 18:54:54 +08:00
|
|
|
|
|
2026-02-20 18:44:43 +08:00
|
|
|
|
// 如果配置了日志文件,添加文件输出
|
|
|
|
|
|
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...) }
|