package cmd import ( "NetworkAuth/config" "NetworkAuth/utils" "NetworkAuth/utils/logger" "io" "os" "path/filepath" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" "gopkg.in/natefinch/lumberjack.v2" ) var cfgFile string // rootCmd 代表没有调用子命令时的基础命令 var rootCmd = &cobra.Command{ Use: "NetworkAuth", Short: "网络授权服务命令行工具", Long: `网络授权服务 (NetworkAuth) 是一个专注于应用鉴权、接口管理和动态逻辑分发的后端系统。 本命令行工具用于启动服务器、管理配置和执行维护任务。`, PersistentPreRun: func(cmd *cobra.Command, args []string) { // 在加载配置前配置logrus用于非HTTP日志 setupLogrusForNonHTTP() }, } // Execute 添加所有子命令到根命令并设置适当的标志 // 这由main.main()调用。只需要对rootCmd执行一次。 func Execute() { err := rootCmd.Execute() if err != nil { os.Exit(1) } } func init() { cobra.OnInitialize(initConfig) // 在这里定义标志和配置设置 rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件路径 (默认为 config.json)") } // setupLogrusForNonHTTP 配置logrus用于非HTTP日志 // 在加载配置文件之前进行基本的logrus设置 func setupLogrusForNonHTTP() { // 设置日志格式 logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: "2006-01-02 15:04:05", FullTimestamp: true, ForceColors: false, DisableColors: true, }) // 设置默认日志级别 logrus.SetLevel(logrus.InfoLevel) // 设置输出目标(稍后会根据配置文件调整) logrus.SetOutput(os.Stdout) // 初始化配置(优先使用命令行参数,否则默认 config.json) // 注意:如果文件不存在,配置系统将在内存中生成默认配置 if cfgFile != "" { config.Init(cfgFile) } else { config.Init("config.json") } // 根据配置文件进一步配置logrus setupLogrusFromConfig() // 初始化HTTP日志处理器 logger.InitLogger() // 记录配置加载完成,使用相对路径或文件名保持一致性 configFile := viper.ConfigFileUsed() if configFile != "" { fileName := utils.DisplayPath(configFile) logrus.WithField("file", fileName).Info("配置文件加载完成") } else { logrus.Info("配置加载完成(内存默认配置)") } } // initConfig 读取配置文件和环境变量 func initConfig() { } // setupLogrusFromConfig 根据配置文件进一步配置logrus // 设置日志级别和输出目标,支持日志切割功能 func setupLogrusFromConfig() { // 设置日志级别 if level := viper.GetString("log.level"); level != "" { if logLevel, err := logrus.ParseLevel(level); err == nil { logrus.SetLevel(logLevel) } } // 设置日志输出目标 logFile := viper.GetString("log.file") if logFile != "" { // 统一转换为绝对路径,避免不同系统或启动目录下出现日志落点不一致。 absPath := filepath.Clean(logFile) if !filepath.IsAbs(absPath) { absPath = filepath.Join(utils.GetRootDir(), absPath) } if normalizedPath, err := filepath.Abs(absPath); err == nil { absPath = normalizedPath } // 确保日志目录存在 logDir := filepath.Dir(absPath) if err := os.MkdirAll(logDir, 0755); err != nil { logrus.WithError(err).Error("创建日志目录失败") return } // 配置lumberjack日志轮转 lumberjackLogger := &lumberjack.Logger{ Filename: absPath, MaxSize: viper.GetInt("log.max_size"), // MB MaxBackups: viper.GetInt("log.max_backups"), // 保留的旧日志文件数量 MaxAge: viper.GetInt("log.max_age"), // 天数 Compress: true, // 压缩旧日志文件 } // 同时输出到控制台和文件(带日志切割) multiWriter := io.MultiWriter(os.Stdout, lumberjackLogger) logrus.SetOutput(multiWriter) logrus.WithFields(logrus.Fields{ "file": utils.DisplayPath(absPath), "max_size": viper.GetInt("log.max_size"), "max_backups": viper.GetInt("log.max_backups"), "max_age": viper.GetInt("log.max_age"), }).Info("日志切割功能已启用") } // 当日志文件路径为空时,保持默认输出到控制台,不创建任何目录 }