package config import ( "encoding/json" "net/url" "os" "strings" "sync" "github.com/coroot/coroot-node-agent/flags" klog "github.com/sirupsen/logrus" "github.com/spf13/viper" ) // Config 统一的配置结构体 // 所有配置项都通过这个结构体访问,内部自动判断优先级 type Config struct { mu sync.RWMutex // Viper 实例(用于读取配置文件) viper *viper.Viper // 命令行参数值(最高优先级) cmdLineValues map[string]interface{} // 环境变量值(第二优先级) envValues map[string]interface{} // 配置文件值(第三优先级) configFileValues map[string]interface{} // 默认值(最低优先级) defaultValues map[string]interface{} // 特殊配置:common.ini 中的值(最高优先级,仅用于 ConfigServer 和 DataServer) commonIniValues map[string]string } var ( globalConfig *Config globalConfigOnce sync.Once ) // Get 获取全局配置实例 func Get() *Config { globalConfigOnce.Do(func() { globalConfig = &Config{ cmdLineValues: make(map[string]interface{}), envValues: make(map[string]interface{}), configFileValues: make(map[string]interface{}), defaultValues: make(map[string]interface{}), commonIniValues: make(map[string]string), } }) return globalConfig } // Init 初始化配置管理器 // 必须在 flags.Parse() 之后调用 func (c *Config) Init(viperInstance *viper.Viper) { c.mu.Lock() defer c.mu.Unlock() c.viper = viperInstance // 1. 收集命令行参数值(最高优先级) c.collectCmdLineValues() // 2. 收集环境变量值(第二优先级) c.collectEnvValues() // 3. 收集配置文件值(第三优先级) c.collectConfigFileValues() // 4. 收集默认值(最低优先级) c.collectDefaultValues() // 5. 收集 common.ini 中的值(特殊处理) c.collectCommonIniValues() // 6. 将配置文件中的值同步到 flags(使 *flags.XXX 在启动时也能反映配置文件优先级) c.syncFlagsValues() } // collectCmdLineValues 收集命令行参数值 func (c *Config) collectCmdLineValues() { // 从 flags 包中读取命令行参数值 c.cmdLineValues["server.config_server"] = *flags.ConfigServer c.cmdLineValues["server.data_server"] = *flags.DataServer c.cmdLineValues["server.server_prefix"] = *flags.ServerPrefix c.cmdLineValues["server.license_key"] = *flags.LicenseKey c.cmdLineValues["server.register_app_to_doop"] = *flags.RegisterAppToDoop c.cmdLineValues["agent.disable_e2e_tracing"] = *flags.DisableE2ETracing c.cmdLineValues["agent.disable_stack_tracing"] = *flags.DisableStackTracing c.cmdLineValues["agent.disable_l7_tracing"] = *flags.DisableL7Tracing c.cmdLineValues["agent.enable_elasticsearch_detection"] = *flags.EnableElasticsearchDetection c.cmdLineValues["agent.run_in_container"] = *flags.RunInContainer c.cmdLineValues["agent.run_in_omniagent"] = *flags.RunInOmniagent c.cmdLineValues["agent.disable_register_host"] = *flags.DisableRegisterHost c.cmdLineValues["agent.enable_license_check"] = *flags.EnableLicenseCheck c.cmdLineValues["agent.send_net_data"] = *flags.SendNetData c.cmdLineValues["network.listen_address"] = *flags.ListenAddress c.cmdLineValues["network.track_public_network"] = *flags.ExternalNetworksWhitelist c.cmdLineValues["network.ephemeral_port_range"] = *flags.EphemeralPortRange c.cmdLineValues["container.cgroupfs_root"] = *flags.CgroupRoot c.cmdLineValues["container.host_dir_path_prefix"] = *flags.HostDirPathPrefix c.cmdLineValues["container.disable_log_parsing"] = *flags.DisableLogParsing c.cmdLineValues["container.disable_pinger"] = *flags.DisablePinger c.cmdLineValues["logging.log_level"] = *flags.LogLevel c.cmdLineValues["logging.console_log"] = *flags.ConsoleLog c.cmdLineValues["logging.log_per_second"] = *flags.LogPerSecond c.cmdLineValues["logging.log_burst"] = *flags.LogBurst c.cmdLineValues["ebpf.ebpf_file_path"] = *flags.EbpfFilePath } // collectEnvValues 收集环境变量值 func (c *Config) collectEnvValues() { // 环境变量映射表:环境变量名 -> 配置键 envMap := map[string]string{ // Server 配置 "CONFIG_SERVER": "server.config_server", "EUSPACE_SERVER_CONFIG_SERVER": "server.config_server", "DATA_SERVER": "server.data_server", "EUSPACE_SERVER_DATA_SERVER": "server.data_server", "SERVER_PREFIX": "server.server_prefix", "EUSPACE_SERVER_SERVER_PREFIX": "server.server_prefix", "LICENSE_KEY": "server.license_key", "EUSPACE_SERVER_LICENSE_KEY": "server.license_key", "REGISTER_APP_TO_DOOP": "server.register_app_to_doop", "EUSPACE_SERVER_REGISTER_APP_TO_DOOP": "server.register_app_to_doop", // Agent 配置 "DISABLE_E2E_TRACING": "agent.disable_e2e_tracing", "EUSPACE_AGENT_DISABLE_E2E_TRACING": "agent.disable_e2e_tracing", "DISABLE_STACK_TRACING": "agent.disable_stack_tracing", "EUSPACE_AGENT_DISABLE_STACK_TRACING": "agent.disable_stack_tracing", "DISABLE_L7_TRACING": "agent.disable_l7_tracing", "EUSPACE_AGENT_DISABLE_L7_TRACING": "agent.disable_l7_tracing", "ENABLE_ES": "agent.enable_elasticsearch_detection", "EUSPACE_AGENT_ENABLE_ELASTICSEARCH_DETECTION": "agent.enable_elasticsearch_detection", "RUN_IN_CONTAINER": "agent.run_in_container", "EUSPACE_AGENT_RUN_IN_CONTAINER": "agent.run_in_container", "RUN_IN_OMNIAGENT": "agent.run_in_omniagent", "EUSPACE_AGENT_RUN_IN_OMNIAGENT": "agent.run_in_omniagent", "DISABLE_REG_HOST": "agent.disable_register_host", "EUSPACE_AGENT_DISABLE_REGISTER_HOST": "agent.disable_register_host", "ENABLE_LICENSE_CHECK": "agent.enable_license_check", "EUSPACE_AGENT_ENABLE_LICENSE_CHECK": "agent.enable_license_check", "SEND_NET_DATA": "agent.send_net_data", "EUSPACE_AGENT_SEND_NET_DATA": "agent.send_net_data", // Network 配置 "LISTEN": "network.listen_address", "EUSPACE_NETWORK_LISTEN_ADDRESS": "network.listen_address", "TRACK_PUBLIC_NETWORK": "network.track_public_network", "EUSPACE_NETWORK_TRACK_PUBLIC_NETWORK": "network.track_public_network", "EPHEMERAL_PORT_RANGE": "network.ephemeral_port_range", "EUSPACE_NETWORK_EPHEMERAL_PORT_RANGE": "network.ephemeral_port_range", // Container 配置 "CGROUPFS_ROOT": "container.cgroupfs_root", "EUSPACE_CONTAINER_CGROUPFS_ROOT": "container.cgroupfs_root", "HOST_DIR_PATH_PREFIX": "container.host_dir_path_prefix", "EUSPACE_CONTAINER_HOST_DIR_PATH_PREFIX": "container.host_dir_path_prefix", "DISABLE_LOG_PARSING": "container.disable_log_parsing", "EUSPACE_CONTAINER_DISABLE_LOG_PARSING": "container.disable_log_parsing", "DISABLE_PINGER": "container.disable_pinger", "EUSPACE_CONTAINER_DISABLE_PINGER": "container.disable_pinger", // Logging 配置 "LOG_LEVEL": "logging.log_level", "EUSPACE_LOGGING_LOG_LEVEL": "logging.log_level", "CONSOLE_LOG": "logging.console_log", "EUSPACE_LOGGING_CONSOLE_LOG": "logging.console_log", "LOG_PER_SECOND": "logging.log_per_second", "EUSPACE_LOGGING_LOG_PER_SECOND": "logging.log_per_second", "LOG_BURST": "logging.log_burst", "EUSPACE_LOGGING_LOG_BURST": "logging.log_burst", // eBPF 配置 "EBPF_FILE": "ebpf.ebpf_file_path", "EUSPACE_EBPF_EBPF_FILE_PATH": "ebpf.ebpf_file_path", // Cloud 配置 "PROVIDER": "cloud.provider", "EUSPACE_CLOUD_PROVIDER": "cloud.provider", "REGION": "cloud.region", "EUSPACE_CLOUD_REGION": "cloud.region", "AVAILABILITY_ZONE": "cloud.availability_zone", "EUSPACE_CLOUD_AVAILABILITY_ZONE": "cloud.availability_zone", "INSTANCE_TYPE": "cloud.instance_type", "EUSPACE_CLOUD_INSTANCE_TYPE": "cloud.instance_type", "INSTANCE_LIFE_CYCLE": "cloud.instance_life_cycle", "EUSPACE_CLOUD_INSTANCE_LIFE_CYCLE": "cloud.instance_life_cycle", } for envKey, configKey := range envMap { if val := os.Getenv(envKey); val != "" { c.envValues[configKey] = val } } } // collectConfigFileValues 收集配置文件值 func (c *Config) collectConfigFileValues() { if c.viper == nil { return } // 从 Viper 中读取所有配置项 allKeys := c.viper.AllKeys() for _, key := range allKeys { if c.viper.IsSet(key) { c.configFileValues[key] = c.viper.Get(key) } } } // collectDefaultValues 收集默认值 func (c *Config) collectDefaultValues() { // 默认值定义 c.defaultValues["server.config_server"] = "" c.defaultValues["server.data_server"] = "" c.defaultValues["server.server_prefix"] = "" c.defaultValues["server.license_key"] = "J45Engw88NeHUZ4Q7qNsK8L47FTH**QvgW113IEnsNaBNMR5zZ**oj/g!!!!" c.defaultValues["server.register_app_to_doop"] = false c.defaultValues["agent.disable_e2e_tracing"] = true c.defaultValues["agent.disable_stack_tracing"] = true c.defaultValues["agent.disable_l7_tracing"] = false c.defaultValues["agent.enable_elasticsearch_detection"] = false c.defaultValues["agent.run_in_container"] = false c.defaultValues["agent.run_in_omniagent"] = false c.defaultValues["agent.disable_register_host"] = false c.defaultValues["agent.enable_license_check"] = true c.defaultValues["agent.send_net_data"] = false c.defaultValues["network.listen_address"] = "0.0.0.0:8123" c.defaultValues["network.track_public_network"] = []string{"0.0.0.0/0"} c.defaultValues["network.ephemeral_port_range"] = "" c.defaultValues["container.cgroupfs_root"] = "/sys/fs/cgroup" c.defaultValues["container.host_dir_path_prefix"] = "" c.defaultValues["container.disable_log_parsing"] = true c.defaultValues["container.disable_pinger"] = false c.defaultValues["logging.log_level"] = "info" c.defaultValues["logging.console_log"] = false c.defaultValues["logging.log_per_second"] = 10.0 c.defaultValues["logging.log_burst"] = 100 c.defaultValues["ebpf.ebpf_file_path"] = "" } // collectCommonIniValues 收集 common.ini 中的值 func (c *Config) collectCommonIniValues() { if !*flags.RunInOmniagent || *flags.CommonIni == "" { return } // 从 common.ini 加载 ConfigServer 和 DataServer // 这个逻辑已经在 flags.go 的 init() 中执行了 // 这里我们只是记录一下 if *flags.ConfigServer != "" { c.commonIniValues["server.config_server"] = *flags.ConfigServer } if *flags.DataServer != "" { c.commonIniValues["server.data_server"] = *flags.DataServer } } // getValue 获取配置值(自动判断优先级) // 优先级:common.ini > 环境变量(非空)> 配置文件(非空)> 默认值 func (c *Config) getValue(key string) interface{} { c.mu.RLock() defer c.mu.RUnlock() // 1. 最高优先级:common.ini 中的值(仅用于 ConfigServer 和 DataServer) if val, ok := c.commonIniValues[key]; ok { if val != "" { return val } } // 2. 环境变量(如果值不为空) if envVal, ok := c.envValues[key]; ok { // 检查环境变量值是否为空 if strVal, ok := envVal.(string); ok { if strVal != "" { if strVal == "true" { return true } else if strVal == "false" { return false } return envVal } } else { // 非字符串类型,直接返回 return envVal } } // 3. 配置文件(如果值不为空) if configVal, ok := c.configFileValues[key]; ok { // 检查配置文件值是否为空 if strVal, ok := configVal.(string); ok { if strVal != "" { return configVal } } else { // 非字符串类型,直接返回 return configVal } } // flags 中的值 if cmdVal, ok := c.cmdLineValues[key]; ok { return cmdVal } // 默认值 if defaultVal, ok := c.defaultValues[key]; ok { return defaultVal } return nil } // Reload 重新加载配置(用于热更新) func (c *Config) Reload() { c.mu.Lock() defer c.mu.Unlock() // 重新收集配置文件值 c.collectConfigFileValues() // 重新收集 common.ini 值 c.collectCommonIniValues() // 同步更新 flags 中的值,确保使用 *flags.XXX 的地方也能获取到最新值 c.syncFlagsValues() } // syncFlagsValues 同步更新 flags 中的值 // 这样即使代码使用 *flags.XXX,也能在热更新后获取到新值 // 注意:调用此方法时,必须已经持有 c.mu 写锁 func (c *Config) syncFlagsValues() { if c.viper == nil { return } // 更新所有可以通过配置文件设置的 flags 值 // 注意:只更新那些没有被命令行参数或环境变量设置的配置项 // 注意:这里直接访问内部字段,不调用 getValue(),避免死锁(因为调用者已经持有写锁) // Server 配置 if c.viper.IsSet("server.config_server") { // 直接访问内部字段,按优先级顺序检查 if val, ok := c.commonIniValues["server.config_server"]; ok && val != "" { *flags.ConfigServer = val } else if envVal, ok := c.envValues["server.config_server"]; ok { if s, ok := envVal.(string); ok && s != "" { *flags.ConfigServer = s } } else if configVal, ok := c.configFileValues["server.config_server"]; ok { if s, ok := configVal.(string); ok && s != "" { *flags.ConfigServer = s } } } if c.viper.IsSet("server.data_server") { if val, ok := c.commonIniValues["server.data_server"]; ok && val != "" { *flags.DataServer = val } else if envVal, ok := c.envValues["server.data_server"]; ok { if s, ok := envVal.(string); ok && s != "" { *flags.DataServer = s } } else if configVal, ok := c.configFileValues["server.data_server"]; ok { if s, ok := configVal.(string); ok && s != "" { *flags.DataServer = s } } } // 辅助函数:按优先级获取值(不获取锁,因为调用者已持有写锁) getValueUnsafe := func(key string) interface{} { // 1. common.ini(最高优先级) if val, ok := c.commonIniValues[key]; ok && val != "" { return val } // 2. 环境变量 if envVal, ok := c.envValues[key]; ok { if strVal, ok := envVal.(string); ok { if strVal != "" { return envVal } } else { return envVal } } // 3. 配置文件 if configVal, ok := c.configFileValues[key]; ok { if strVal, ok := configVal.(string); ok { if strVal != "" { return configVal } } else { return configVal } } // 4. 默认值 if defaultVal, ok := c.defaultValues[key]; ok { return defaultVal } return nil } if c.viper.IsSet("server.server_prefix") { if val := getValueUnsafe("server.server_prefix"); val != nil { if s, ok := val.(string); ok { *flags.ServerPrefix = s // 重新计算 TracesEndpoint,使 /data/receive 也使用更新后的前缀 dataServer := *flags.DataServer if dataServer != "" { if !strings.HasPrefix(dataServer, "http") { dataServer = "http://" + dataServer } if u, err := url.Parse(dataServer); err == nil && u != nil { *flags.TracesEndpoint = u.JoinPath(s + "/api/v2/data/receive") } } } } } if c.viper.IsSet("server.license_key") { if val := getValueUnsafe("server.license_key"); val != nil { if s, ok := val.(string); ok { *flags.LicenseKey = s } } } // Logging 配置 if c.viper.IsSet("logging.log_level") { if val := getValueUnsafe("logging.log_level"); val != nil { if s, ok := val.(string); ok { *flags.LogLevel = s } } } if c.viper.IsSet("logging.console_log") { if val := getValueUnsafe("logging.console_log"); val != nil { if b, ok := val.(bool); ok { *flags.ConsoleLog = b } } } // Agent 配置 if c.viper.IsSet("agent.disable_e2e_tracing") { if val := getValueUnsafe("agent.disable_e2e_tracing"); val != nil { if b, ok := val.(bool); ok { *flags.DisableE2ETracing = b } } } if c.viper.IsSet("agent.disable_stack_tracing") { if val := getValueUnsafe("agent.disable_stack_tracing"); val != nil { if b, ok := val.(bool); ok { *flags.DisableStackTracing = b } } } if c.viper.IsSet("agent.disable_l7_tracing") { if val := getValueUnsafe("agent.disable_l7_tracing"); val != nil { if b, ok := val.(bool); ok { *flags.DisableL7Tracing = b } } } if c.viper.IsSet("agent.disable_register_host") { if val := getValueUnsafe("agent.disable_register_host"); val != nil { if b, ok := val.(bool); ok { *flags.DisableRegisterHost = b } } } if c.viper.IsSet("agent.enable_license_check") { if val := getValueUnsafe("agent.enable_license_check"); val != nil { if b, ok := val.(bool); ok { *flags.EnableLicenseCheck = b } } } if c.viper.IsSet("agent.send_net_data") { if val := getValueUnsafe("agent.send_net_data"); val != nil { if b, ok := val.(bool); ok { *flags.SendNetData = b } } } // Network 配置 if c.viper.IsSet("network.listen_address") { if val := getValueUnsafe("network.listen_address"); val != nil { if s, ok := val.(string); ok { *flags.ListenAddress = s } } } // Container 配置 if c.viper.IsSet("container.cgroupfs_root") { if val := getValueUnsafe("container.cgroupfs_root"); val != nil { if s, ok := val.(string); ok { *flags.CgroupRoot = s } } } // 注意:这里只更新了部分常用的配置项 // 如果需要更新其他配置项,可以继续添加 } // 便捷方法:获取各种类型的配置值 func (c *Config) LogLevel() string { val := c.getValue("logging.log_level") if s, ok := val.(string); ok && s != "" { return s } return "info" } func (c *Config) ConsoleLog() bool { val := c.getValue("logging.console_log") if b, ok := val.(bool); ok { return b } return false } func (c *Config) ConfigServer() string { val := c.getValue("server.config_server") if s, ok := val.(string); ok { return s } return "" } func (c *Config) DataServer() string { val := c.getValue("server.data_server") if s, ok := val.(string); ok { return s } return "" } func (c *Config) ServerPrefix() string { val := c.getValue("server.server_prefix") if s, ok := val.(string); ok { return s } return "" } func (c *Config) LicenseKey() string { val := c.getValue("server.license_key") if s, ok := val.(string); ok { return s } return "J45Engw88NeHUZ4Q7qNsK8L47FTH**QvgW113IEnsNaBNMR5zZ**oj/g!!!!" } func (c *Config) DisableRegisterHost() bool { val := c.getValue("agent.disable_register_host") if b, ok := val.(bool); ok { return b } return false } func (c *Config) EnableLicenseCheck() bool { val := c.getValue("agent.enable_license_check") if b, ok := val.(bool); ok { return b } return true } func (c *Config) SendNetData() bool { val := c.getValue("agent.send_net_data") if b, ok := val.(bool); ok { return b } return false } func (c *Config) DisableE2ETracing() bool { val := c.getValue("agent.disable_e2e_tracing") if b, ok := val.(bool); ok { return b } return true } func (c *Config) DisableStackTracing() bool { val := c.getValue("agent.disable_stack_tracing") if b, ok := val.(bool); ok { return b } return true } func (c *Config) DisableL7Tracing() bool { val := c.getValue("agent.disable_l7_tracing") if b, ok := val.(bool); ok { return b } return false } func (c *Config) RunInContainer() bool { val := c.getValue("agent.run_in_container") if b, ok := val.(bool); ok { return b } return false } func (c *Config) RunInOmniagent() bool { // RunInOmniagent 不支持热更新,直接从 flags 获取 return *flags.RunInOmniagent } func (c *Config) ListenAddress() string { val := c.getValue("network.listen_address") if s, ok := val.(string); ok { return s } return "0.0.0.0:8123" } func (c *Config) CgroupRoot() string { val := c.getValue("container.cgroupfs_root") if s, ok := val.(string); ok { return s } return "/sys/fs/cgroup" } // AgentRule 配置文件 agent.rules 中的单条规则 type AgentRule struct { AppName string `json:"app_name"` RuleType int `json:"rule_type"` Filters string `json:"filters"` } // AgentRules 从配置文件读取 agent.rules JSON 字符串,解析后返回规则列表 // 若未配置或解析失败,返回 nil func (c *Config) AgentRules() []AgentRule { val := c.getValue("agent.rules") if val == nil { return nil } s, ok := val.(string) if !ok || s == "" { return nil } var rules []AgentRule if err := json.Unmarshal([]byte(s), &rules); err != nil { klog.Errorf("failed to parse agent.rules: %v", err) return nil } return rules } // TracesEndpoint 获取 TracesEndpoint // 优先级:命令行参数/环境变量 > 根据 DataServer 和 ServerPrefix 计算 // 如果都为空,返回 nil func (c *Config) TracesEndpoint() *url.URL { // 1. 优先使用命令行参数或环境变量设置的 TracesEndpoint if *flags.TracesEndpoint != nil { return *flags.TracesEndpoint } // 2. 如果 TracesEndpoint 未设置,根据 DataServer 和 ServerPrefix 计算 dataServer := c.DataServer() if dataServer == "" { return nil } // 如果 DataServer 没有 http/https 前缀,添加 http:// dataServerStr := dataServer if !strings.HasPrefix(dataServerStr, "http://") && !strings.HasPrefix(dataServerStr, "https://") { dataServerStr = "http://" + dataServerStr } // 解析 DataServer URL dataServerURL, err := url.Parse(dataServerStr) if err != nil || dataServerURL == nil { return nil } // 构建 TracesEndpoint:DataServer + ServerPrefix + "/api/v2/data/receive" serverPrefix := c.ServerPrefix() path := serverPrefix + "/api/v2/data/receive" return dataServerURL.JoinPath(path) }