This commit is contained in:
2026-02-14 05:36:00 +08:00
commit d7cd899983
37 changed files with 4169 additions and 0 deletions

344
internal/log/logger.go Normal file
View File

@@ -0,0 +1,344 @@
package log
import (
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"sync"
"time"
"github.com/fatih/color"
)
// 日志级别
type Level int
const (
DebugLevel Level = iota
InfoLevel
WarnLevel
ErrorLevel
FatalLevel
)
// 级别名称
var levelNames = map[Level]string{
DebugLevel: "DEBUG",
InfoLevel: "INFO",
WarnLevel: "WARN",
ErrorLevel: "ERROR",
FatalLevel: "FATAL",
}
// 级别简写
var levelShort = map[Level]string{
DebugLevel: "[d]",
InfoLevel: "[i]",
WarnLevel: "[w]",
ErrorLevel: "[e]",
FatalLevel: "[f]",
}
// 级别颜色
var levelColor = map[Level]func(format string, a ...interface{}) string{
DebugLevel: color.CyanString, // 青色
InfoLevel: color.GreenString, // 绿色
WarnLevel: color.YellowString, // 黄色
ErrorLevel: color.RedString, // 红色
FatalLevel: color.MagentaString, // 品红
}
// Logger 日志器结构体
type Logger struct {
mu sync.Mutex
consoleOut io.Writer // 控制台输出
fileOut io.Writer // 文件输出
minLevel Level // 最小输出级别
showColor bool // 是否显示颜色
showCaller bool // 是否显示调用者信息
callerSkip int // 调用者跳过的层级
timeFormat string // 时间格式
}
// 默认日志器实例
var defaultLogger *Logger
const (
defaultTimeFormat = "2006-01-02 15:04:05"
logFile = "/var/log/sunhpc/sunhpc.log"
)
// Init 初始化日志系统
func Init(verbose bool) {
// 确保日志目录存在
if err := os.MkdirAll(filepath.Dir(logFile), 0755); err != nil {
fmt.Fprintf(os.Stderr, "创建日志目录失败: %v\n", err)
os.Exit(1)
}
// 打开日志文件
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "打开日志文件失败: %v\n", err)
os.Exit(1)
}
// 控制台输出
consoleOut := os.Stdout
// 创建日志器
defaultLogger = &Logger{
consoleOut: consoleOut,
fileOut: file,
minLevel: InfoLevel,
showColor: true,
showCaller: false,
callerSkip: 2,
timeFormat: defaultTimeFormat,
}
// 详细模式下显示调试信息
if verbose {
defaultLogger.minLevel = DebugLevel
defaultLogger.showCaller = true
}
// 初始化颜色支持
if runtime.GOOS == "windows" {
color.NoColor = false
}
}
// log 核心日志输出方法
func (l *Logger) log(level Level, format string, args ...interface{}) {
if level < l.minLevel {
return
}
l.mu.Lock()
defer l.mu.Unlock()
// 生成时间戳
timestamp := time.Now().Format(l.timeFormat)
// 获取调用者信息
caller := ""
if l.showCaller {
_, file, line, ok := runtime.Caller(l.callerSkip)
if ok {
// 只保留文件名和行号
file = filepath.Base(file)
caller = fmt.Sprintf(" %s:%d", file, line)
}
}
// 格式化消息
var message string
if format == "" {
message = fmt.Sprint(args...)
} else {
message = fmt.Sprintf(format, args...)
}
// ---- 控制台输出(带颜色和简写)----
if l.consoleOut != nil {
// 获取级别简写
shortPrefix := levelShort[level]
// 构建控制台行
var consoleLine string
if l.showColor {
// 带颜色输出 - 简写有颜色,时间戳灰色
colorFunc := levelColor[level]
consoleLine = fmt.Sprintf("%s %s %s",
color.HiBlackString(timestamp), // 时间戳灰色
colorFunc(shortPrefix), // 级别简写彩色
message) // 消息普通颜色
} else {
// 不带颜色输出
consoleLine = fmt.Sprintf("%s %s %s",
timestamp,
shortPrefix,
message)
}
// 添加调用者信息(灰色)
if caller != "" {
if l.showColor {
consoleLine += fmt.Sprintf(" %s", color.HiBlackString(caller))
} else {
consoleLine += fmt.Sprintf(" %s", caller)
}
}
fmt.Fprintln(l.consoleOut, consoleLine)
}
// ---- 文件输出(完整格式)----
if l.fileOut != nil {
// 获取级别全名
levelName := levelNames[level]
// 文件使用完整格式:时间 [级别] 消息 调用者
fileLine := fmt.Sprintf("%s [%s] %s%s\n",
timestamp,
levelName,
message,
caller)
fmt.Fprint(l.fileOut, fileLine)
}
// 致命错误退出程序
if level == FatalLevel {
os.Exit(1)
}
}
// 全局日志函数
// Debug 调试日志
func Debug(args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(DebugLevel, "", args...)
}
}
// Debugf 格式化调试日志
func Debugf(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(DebugLevel, format, args...)
}
}
// Info 信息日志
func Info(args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(InfoLevel, "", args...)
}
}
// Infof 格式化信息日志
func Infof(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(InfoLevel, format, args...)
}
}
// Warn 警告日志
func Warn(args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(WarnLevel, "", args...)
}
}
// Warnf 格式化警告日志
func Warnf(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(WarnLevel, format, args...)
}
}
// Error 错误日志
func Error(args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(ErrorLevel, "", args...)
}
}
// Errorf 格式化错误日志
func Errorf(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(ErrorLevel, format, args...)
}
}
// Fatal 致命错误日志,输出后退出程序
func Fatal(args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(FatalLevel, "", args...)
}
}
// Fatalf 格式化致命错误日志,输出后退出程序
func Fatalf(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(FatalLevel, format, args...)
}
}
// Writer 返回一个 io.Writer可将子命令的输出写入日志Debug级别
func Writer() *io.PipeWriter {
r, w := io.Pipe()
go func() {
buf := make([]byte, 1024)
for {
n, err := r.Read(buf)
if n > 0 {
Debug(string(buf[:n]))
}
if err != nil {
break
}
}
}()
return w
}
// SetLevel 设置日志级别
func SetLevel(level Level) {
if defaultLogger != nil {
defaultLogger.mu.Lock()
defer defaultLogger.mu.Unlock()
defaultLogger.minLevel = level
}
}
// EnableColor 启用/禁用颜色输出
func EnableColor(enable bool) {
if defaultLogger != nil {
defaultLogger.mu.Lock()
defer defaultLogger.mu.Unlock()
defaultLogger.showColor = enable
}
}
// EnableCaller 启用/禁用调用者信息
func EnableCaller(enable bool) {
if defaultLogger != nil {
defaultLogger.mu.Lock()
defer defaultLogger.mu.Unlock()
defaultLogger.showCaller = enable
}
}
// SetTimeFormat 设置时间格式
func SetTimeFormat(format string) {
if defaultLogger != nil {
defaultLogger.mu.Lock()
defer defaultLogger.mu.Unlock()
defaultLogger.timeFormat = format
}
}
// Sync 同步日志文件
func Sync() {
if defaultLogger != nil && defaultLogger.fileOut != nil {
if f, ok := defaultLogger.fileOut.(*os.File); ok {
f.Sync()
}
}
}
// Close 关闭日志文件
func Close() error {
if defaultLogger != nil && defaultLogger.fileOut != nil {
if f, ok := defaultLogger.fileOut.(*os.File); ok {
return f.Close()
}
}
return nil
}