package utils import ( "fmt" "os" "path/filepath" "sync" "time" "github.com/evil7/hostsync/config" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( fileLogger *zap.Logger fileSugar *zap.SugaredLogger consoleLogger *zap.Logger consoleSugar *zap.SugaredLogger debugMode bool logMutex sync.Mutex ) // InitLogger 初始化日志系统 func InitLogger() error { if config.AppConfig == nil { return nil } // 根据配置设置日志级别 level := zapcore.InfoLevel if config.AppConfig.LogLevel != "" { switch config.AppConfig.LogLevel { case "debug": level = zapcore.DebugLevel case "info": level = zapcore.InfoLevel case "warning", "warn": level = zapcore.WarnLevel case "error": level = zapcore.ErrorLevel case "silent": level = zapcore.ErrorLevel + 1 // 静默模式,不记录任何日志 } } // 创建控制台logger配置(只用于错误输出) consoleConfig := zap.Config{ Level: zap.NewAtomicLevelAt(zapcore.ErrorLevel), // 只显示错误 Development: false, Encoding: "console", EncoderConfig: zapcore.EncoderConfig{ MessageKey: "msg", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.CapitalLevelEncoder, }, OutputPaths: []string{"stderr"}, ErrorOutputPaths: []string{"stderr"}, } var err error consoleLogger, err = consoleConfig.Build(zap.AddStacktrace(zapcore.DPanicLevel)) if err != nil { return fmt.Errorf("创建控制台logger失败: %v", err) } consoleSugar = consoleLogger.Sugar() // 如果配置了文件路径,创建文件logger if config.AppConfig.LogPath != "" { // 确保日志目录存在 if err := os.MkdirAll(config.AppConfig.LogPath, 0755); err != nil { return fmt.Errorf("创建日志目录失败: %v", err) } logFileName := filepath.Join(config.AppConfig.LogPath, "hostsync.log") // 检查并轮转日志文件 if err := rotateLogIfNeeded(logFileName); err != nil { return fmt.Errorf("轮转日志文件失败: %v", err) } // 创建文件logger配置 fileConfig := zap.Config{ Level: zap.NewAtomicLevelAt(level), Development: false, Encoding: "console", EncoderConfig: zapcore.EncoderConfig{ TimeKey: "time", LevelKey: "level", MessageKey: "msg", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.CapitalLevelEncoder, EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05")) }, }, OutputPaths: []string{logFileName}, ErrorOutputPaths: []string{logFileName}, } fileLogger, err = fileConfig.Build(zap.AddStacktrace(zapcore.DPanicLevel)) if err != nil { return fmt.Errorf("创建文件logger失败: %v", err) } fileSugar = fileLogger.Sugar() } return nil } // rotateLogIfNeeded 检查并轮转日志文件 func rotateLogIfNeeded(logFileName string) error { info, err := os.Stat(logFileName) if os.IsNotExist(err) { return nil } if err != nil { return err } // 检查文件大小是否超过10MB if info.Size() < 10*1024*1024 { return nil } // 轮转日志文件 backupName := fmt.Sprintf("%s.%s", logFileName, time.Now().Format("20060102_150405")) if err := os.Rename(logFileName, backupName); err != nil { return err } // 异步清理旧日志文件 go cleanOldLogs(filepath.Dir(logFileName)) return nil } // cleanOldLogs 清理旧的日志备份文件,保留最近5个 func cleanOldLogs(logDir string) { files, err := filepath.Glob(filepath.Join(logDir, "hostsync.log.*")) if err != nil || len(files) <= 5 { return } // 删除最旧的文件 for i := 0; i < len(files)-5; i++ { os.Remove(files[i]) } } // SetDebugMode 设置调试模式 func SetDebugMode(debug bool) { debugMode = debug } // CloseLogger 关闭日志系统 func CloseLogger() { if fileLogger != nil { fileLogger.Sync() } if consoleLogger != nil { consoleLogger.Sync() } } // LogInfo 记录信息日志(只记录到文件) func LogInfo(format string, args ...interface{}) { if fileSugar != nil { fileSugar.Infof(format, args...) } } // LogError 记录错误日志(记录到文件和控制台) func LogError(format string, args ...interface{}) { if fileSugar != nil { fileSugar.Errorf(format, args...) } if consoleSugar != nil { consoleSugar.Errorf(format, args...) } else { fmt.Fprintf(os.Stderr, format+"\n", args...) } } // LogDebug 记录调试日志(只记录到文件) func LogDebug(format string, args ...interface{}) { if fileSugar != nil { fileSugar.Debugf(format, args...) } else if debugMode { fmt.Printf("DEBUG: "+format+"\n", args...) } } // LogWarning 记录警告日志(只记录到文件) func LogWarning(format string, args ...interface{}) { if fileSugar != nil { fileSugar.Warnf(format, args...) } } // LogSuccess 记录成功日志(只记录到文件) func LogSuccess(format string, args ...interface{}) { if fileSugar != nil { fileSugar.Infof(format, args...) } } // LogFatal 记录致命错误并退出(记录到文件和控制台) func LogFatal(format string, args ...interface{}) { if fileSugar != nil { fileSugar.Fatalf(format, args...) } else { fmt.Fprintf(os.Stderr, format+"\n", args...) os.Exit(1) } } // LogFileOnly 只记录到文件,不显示在控制台 func LogFileOnly(format string, args ...interface{}) { if fileSugar != nil { fileSugar.Infof(format, args...) } } // LogResult 用户界面结果输出(直接输出,不使用日志系统) func LogResult(format string, args ...interface{}) { fmt.Printf(format, args...) }