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 }