package config import ( "fmt" "github.com/coroot/coroot-node-agent/utils" "strings" "sync" klog "github.com/sirupsen/logrus" "github.com/spf13/viper" "github.com/coroot/coroot-node-agent/flags" ) var ( configOnce sync.Once configInstance *viper.Viper // 运行时配置(用于读取) configMutex sync.RWMutex cmdLineFlagsSet map[string]bool // 记录通过命令行参数设置的配置项 cmdLineFlagsSetOnce sync.Once ) // InitConfig 初始化 Viper 配置 func InitConfig(configPath string) error { var err error configOnce.Do(func() { v := viper.New() // 设置配置文件路径 if configPath != "" { v.SetConfigFile(configPath) } else { v.SetConfigFile(utils.GetDefaultConfigPath()) } // 设置环境变量前缀和键替换规则 v.SetEnvPrefix("EUSPACE") // 设置环境变量键替换规则:将点号替换为下划线 v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) v.AutomaticEnv() // 读取配置文件 // 注意:配置文件读取失败(不存在、格式错误、权限问题等)都不是致命错误 // 程序会继续启动,使用默认值、命令行参数和环境变量 if err = v.ReadInConfig(); err != nil { // 所有配置文件读取错误都视为非致命错误,只记录警告 if _, ok := err.(viper.ConfigFileNotFoundError); ok { klog.Warnf("⚠️ [CONFIG] Config file not found, using defaults: %v", err) } else { klog.Warnf("⚠️ [CONFIG] Error reading config file (will use defaults): %v", err) } err = nil // 不返回错误,允许程序继续启动 } else { actualConfigFile := v.ConfigFileUsed() klog.Infof("✓ [CONFIG] Using config file: %s", actualConfigFile) // 启动配置文件轮询监听 startConfigFileWatcher(actualConfigFile, v) } // 监控 common.ini 文件(如果 RunInOmniagent 为 true) // 注意:RunInOmniagent 是启动时确定的标志,不支持热更新 // 只有在启动时 RunInOmniagent 为 true 时,才会启动此监控 if *flags.RunInOmniagent && *flags.CommonIni != "" { watchCommonIni(*flags.CommonIni) } configInstance = v // 初始化 Config 管理器 cfg := Get() cfg.Init(v) // 应用初始配置(首次加载,可以覆盖默认值) // 注意:现在通过 Config 管理器统一处理,applyConfig 主要用于向后兼容 applyConfig(v, false) }) return err } // GetConfig 获取配置实例 func GetConfig() *viper.Viper { if configInstance == nil { // 如果未初始化,使用默认配置 InitConfig("") } return configInstance } // ReloadConfig 手动重新加载配置 func ReloadConfig() error { configMutex.Lock() defer configMutex.Unlock() if configInstance == nil { return fmt.Errorf("config not initialized") } if err := configInstance.ReadInConfig(); err != nil { return fmt.Errorf("error reading config file: %w", err) } applyConfig(configInstance, false) return nil } // GetConfigValue 获取配置值(支持热加载) func GetConfigValue(key string) interface{} { configMutex.RLock() defer configMutex.RUnlock() if configInstance == nil { return nil } return configInstance.Get(key) }