diff --git a/core/app_config/AppConfig.cpp b/core/app_config/AppConfig.cpp index a90aa9f199..2e939bcfab 100644 --- a/core/app_config/AppConfig.cpp +++ b/core/app_config/AppConfig.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "boost/filesystem.hpp" @@ -161,9 +162,9 @@ DEFINE_FLAG_STRING(metrics_report_method, "method to report metrics (default none, means logtail will not report metrics)", "sls"); -DEFINE_FLAG_STRING(loong_collector_operator_service, "loong collector operator service", ""); -DEFINE_FLAG_INT32(loong_collector_operator_service_port, "loong collector operator service port", 8888); -DEFINE_FLAG_INT32(loong_collector_k8s_meta_service_port, "loong collector operator service port", 9000); +DEFINE_FLAG_STRING(operator_service, "loong collector operator service", ""); +DEFINE_FLAG_INT32(operator_service_port, "loong collector operator service port", 8888); +DEFINE_FLAG_INT32(k8s_meta_service_port, "loong collector operator service port", 9000); DEFINE_FLAG_STRING(_pod_name_, "agent pod name", ""); DEFINE_FLAG_STRING(app_info_file, "", "app_info.json"); @@ -210,14 +211,21 @@ const uint32_t NO_FALL_BACK_FAIL_PERCENTAGE = 10; const uint32_t SLOW_FALL_BACK_FAIL_PERCENTAGE = 40; std::string AppConfig::sLocalConfigDir = "local"; + +const std::string LOONGCOLLECTOR_ENV_PREFIX = "LOONG_"; + +std::string GetLoongcollectorEnv(const std::string& flagName) { + return LOONGCOLLECTOR_ENV_PREFIX + ToUpperCaseString(flagName); +} + void CreateAgentDir() { try { - const char* value = getenv("logtail_mode"); + const char* value = getenv("LOGTAIL_MODE"); if (value != NULL) { STRING_FLAG(logtail_mode) = StringTo(value); } } catch (const exception& e) { - std::cout << "load config from env error, env_name:logtail_mode, error:" << e.what() << std::endl; + std::cout << "load config from env error, env_name:LOGTAIL_MODE, error:" << e.what() << std::endl; } if (BOOL_FLAG(logtail_mode)) { return; @@ -226,7 +234,8 @@ void CreateAgentDir() { Json::Value emptyJson; #define PROCESSDIRFLAG(flag_name) \ try { \ - const char* value = getenv(#flag_name); \ + const auto env_name = GetLoongcollectorEnv(#flag_name); \ + const char* value = getenv(env_name.c_str()); \ if (value != NULL) { \ STRING_FLAG(flag_name) = StringTo(value); \ } \ @@ -247,11 +256,11 @@ void CreateAgentDir() { } \ } - PROCESSDIRFLAG(loongcollector_conf_dir); - PROCESSDIRFLAG(loongcollector_log_dir); - PROCESSDIRFLAG(loongcollector_data_dir); - PROCESSDIRFLAG(loongcollector_run_dir); - PROCESSDIRFLAG(loongcollector_third_party_dir); + PROCESSDIRFLAG(conf_dir); + PROCESSDIRFLAG(logs_dir); + PROCESSDIRFLAG(data_dir); + PROCESSDIRFLAG(run_dir); + PROCESSDIRFLAG(third_party_dir); } std::string GetAgentThirdPartyDir() { @@ -262,7 +271,7 @@ std::string GetAgentThirdPartyDir() { if (BOOL_FLAG(logtail_mode)) { dir = AppConfig::GetInstance()->GetLoongcollectorConfDir(); } else { - dir = STRING_FLAG(loongcollector_third_party_dir) + PATH_SEPARATOR; + dir = STRING_FLAG(third_party_dir) + PATH_SEPARATOR; } return dir; } @@ -278,7 +287,7 @@ std::string GetAgentLogDir() { if (BOOL_FLAG(logtail_mode)) { dir = GetProcessExecutionDir(); } else { - dir = STRING_FLAG(loongcollector_log_dir) + PATH_SEPARATOR; + dir = STRING_FLAG(logs_dir) + PATH_SEPARATOR; } #endif return dir; @@ -372,7 +381,7 @@ std::string GetAgentDataDir() { if (BOOL_FLAG(logtail_mode)) { dir = AppConfig::GetInstance()->GetLoongcollectorConfDir() + PATH_SEPARATOR + "checkpoint"; } else { - dir = STRING_FLAG(loongcollector_data_dir) + PATH_SEPARATOR; + dir = STRING_FLAG(data_dir) + PATH_SEPARATOR; } #endif if (!CheckExistance(dir)) { @@ -396,7 +405,7 @@ std::string GetAgentConfDir() { if (BOOL_FLAG(logtail_mode)) { dir = GetProcessExecutionDir(); } else { - dir = STRING_FLAG(loongcollector_conf_dir) + PATH_SEPARATOR; + dir = STRING_FLAG(conf_dir) + PATH_SEPARATOR; } #endif return dir; @@ -413,7 +422,7 @@ std::string GetAgentRunDir() { if (BOOL_FLAG(logtail_mode)) { dir = GetProcessExecutionDir(); } else { - dir = STRING_FLAG(loongcollector_run_dir) + PATH_SEPARATOR; + dir = STRING_FLAG(run_dir) + PATH_SEPARATOR; } #endif return dir; @@ -841,27 +850,6 @@ bool LoadSingleValueEnvConfig(const char* envKey, T& configValue, const T minVal return false; } -/** - * @brief 从环境变量加载配置值(如果存在) - * - * @tparam T 配置值的类型 - * @param envKey 环境变量的键 - * @param cfgValue 配置值的引用,如果环境变量存在,将被更新 - */ -template -void LoadEnvValueIfExisting(const char* envKey, T& cfgValue) { - try { - const char* value = getenv(envKey); - if (value != NULL) { - T val = StringTo(value); - cfgValue = val; - LOG_INFO(sLogger, ("load config from env", envKey)("value", val)); - } - } catch (const std::exception& e) { - LOG_WARNING(sLogger, ("load config from env error", envKey)("error", e.what())); - } -} - void AppConfig::LoadEnvResourceLimit() { LoadSingleValueEnvConfig("cpu_usage_limit", mCpuUsageUpLimit, (float)0.4); LoadSingleValueEnvConfig("mem_usage_limit", mMemUsageUpLimit, (int64_t)384); @@ -1457,11 +1445,7 @@ void AppConfig::InitEnvMapping(const std::string& envStr, std::map sIgnoreFlagSet = {"loongcollector_conf_dir", - "loongcollector_log_dir", - "loongcollector_data_dir", - "loongcollector_run_dir", - "logtail_mode"}; + static set sIgnoreFlagSet = {"conf_dir", "logs_dir", "data_dir", "run_dir", "logtail_mode"}; if (sIgnoreFlagSet.find(flagName) != sIgnoreFlagSet.end()) { return; } @@ -1508,9 +1492,43 @@ void AppConfig::ParseEnvToFlags() { } } #endif + std::unordered_set sIgnoreFlagSet = {"buffer_file_path", + "check_point_filename", + "data_server_port", + "host_path_blacklist", + "process_thread_count", + "send_request_concurrency", + "check_point_dump_interval", + "check_point_max_count", + "enable_root_path_collection", + "ilogtail_config", + "ilogtail_discard_interval", + "default_tail_limit_kb", + "logreader_max_rotate_queue_size", + "force_release_deleted_file_fd_timeout", + "batch_send_interval", + "ALIYUN_LOG_FILE_TAGS", + "default_container_host_path", + "default_max_inotify_watch_num", + "enable_full_drain_mode", + "ilogtail_discard_old_data", + "timeout_interval", + "enable_env_ref_in_config", + "max_watch_dir_count", + "polling_max_stat_count", + "polling_max_stat_count_per_config", + "polling_max_stat_count_per_dir"}; for (const auto& iter : envMapping) { - const std::string& key = iter.first; const std::string& value = iter.second; + std::string key = iter.first; + // Skip if key is not in ignore set and doesn't start with prefix + if (sIgnoreFlagSet.find(key) == sIgnoreFlagSet.end() && !StartWith(key, LOONGCOLLECTOR_ENV_PREFIX)) { + continue; + } + // Convert to lowercase if key has prefix + if (StartWith(key, LOONGCOLLECTOR_ENV_PREFIX)) { + key = ToLowerCaseString(key.substr(LOONGCOLLECTOR_ENV_PREFIX.size())); + } SetConfigFlag(key, value); // 尝试解析为 double char* end; diff --git a/core/common/LogtailCommonFlags.cpp b/core/common/LogtailCommonFlags.cpp index 783a58aaef..07c19a3869 100644 --- a/core/common/LogtailCommonFlags.cpp +++ b/core/common/LogtailCommonFlags.cpp @@ -122,8 +122,8 @@ DEFINE_FLAG_STRING(default_container_host_path, "", "/logtail_host"); #endif // dir -DEFINE_FLAG_STRING(loongcollector_conf_dir, "loongcollector config dir", "conf"); -DEFINE_FLAG_STRING(loongcollector_log_dir, "loongcollector log dir", "log"); -DEFINE_FLAG_STRING(loongcollector_data_dir, "loongcollector data dir", "data"); -DEFINE_FLAG_STRING(loongcollector_run_dir, "loongcollector run dir", "run"); -DEFINE_FLAG_STRING(loongcollector_third_party_dir, "loongcollector third party dir", "thirdparty"); +DEFINE_FLAG_STRING(conf_dir, "loongcollector config dir", "conf"); +DEFINE_FLAG_STRING(logs_dir, "loongcollector log dir", "log"); +DEFINE_FLAG_STRING(data_dir, "loongcollector data dir", "data"); +DEFINE_FLAG_STRING(run_dir, "loongcollector run dir", "run"); +DEFINE_FLAG_STRING(third_party_dir, "loongcollector third party dir", "thirdparty"); diff --git a/core/common/LogtailCommonFlags.h b/core/common/LogtailCommonFlags.h index b1e64089c4..6c1370cfc9 100644 --- a/core/common/LogtailCommonFlags.h +++ b/core/common/LogtailCommonFlags.h @@ -53,8 +53,8 @@ DECLARE_FLAG_INT32(timeout_interval); DECLARE_FLAG_STRING(default_container_host_path); -DECLARE_FLAG_STRING(loongcollector_conf_dir); -DECLARE_FLAG_STRING(loongcollector_log_dir); -DECLARE_FLAG_STRING(loongcollector_data_dir); -DECLARE_FLAG_STRING(loongcollector_run_dir); -DECLARE_FLAG_STRING(loongcollector_third_party_dir); +DECLARE_FLAG_STRING(conf_dir); +DECLARE_FLAG_STRING(logs_dir); +DECLARE_FLAG_STRING(data_dir); +DECLARE_FLAG_STRING(run_dir); +DECLARE_FLAG_STRING(third_party_dir); diff --git a/core/metadata/K8sMetadata.h b/core/metadata/K8sMetadata.h index a21fad09e4..a6789b05b5 100644 --- a/core/metadata/K8sMetadata.h +++ b/core/metadata/K8sMetadata.h @@ -21,8 +21,8 @@ #include "common/Flags.h" #include "common/LRUCache.h" -DECLARE_FLAG_STRING(loong_collector_operator_service); -DECLARE_FLAG_INT32(loong_collector_k8s_meta_service_port); +DECLARE_FLAG_STRING(operator_service); +DECLARE_FLAG_INT32(k8s_meta_service_port); namespace logtail { @@ -59,8 +59,8 @@ class K8sMetadata { std::string mServiceHost; int32_t mServicePort; K8sMetadata(size_t cacheSize) : containerCache(cacheSize, 0), ipCache(cacheSize, 0) { - mServiceHost = STRING_FLAG(loong_collector_operator_service); - mServicePort = INT32_FLAG(loong_collector_k8s_meta_service_port); + mServiceHost = STRING_FLAG(operator_service); + mServicePort = INT32_FLAG(k8s_meta_service_port); } K8sMetadata(const K8sMetadata&) = delete; K8sMetadata& operator=(const K8sMetadata&) = delete; diff --git a/core/prometheus/PrometheusInputRunner.cpp b/core/prometheus/PrometheusInputRunner.cpp index 673f7385f0..57ef055b05 100644 --- a/core/prometheus/PrometheusInputRunner.cpp +++ b/core/prometheus/PrometheusInputRunner.cpp @@ -38,15 +38,15 @@ using namespace std; -DECLARE_FLAG_STRING(loong_collector_operator_service); -DECLARE_FLAG_INT32(loong_collector_operator_service_port); +DECLARE_FLAG_STRING(operator_service); +DECLARE_FLAG_INT32(operator_service_port); DECLARE_FLAG_STRING(_pod_name_); namespace logtail { PrometheusInputRunner::PrometheusInputRunner() - : mServiceHost(STRING_FLAG(loong_collector_operator_service)), - mServicePort(INT32_FLAG(loong_collector_operator_service_port)), + : mServiceHost(STRING_FLAG(operator_service)), + mServicePort(INT32_FLAG(operator_service_port)), mPodName(STRING_FLAG(_pod_name_)), mEventPool(true), mUnRegisterMs(0) { diff --git a/core/unittest/app_config/AppConfigUnittest.cpp b/core/unittest/app_config/AppConfigUnittest.cpp index e1224d201a..bba92f4564 100644 --- a/core/unittest/app_config/AppConfigUnittest.cpp +++ b/core/unittest/app_config/AppConfigUnittest.cpp @@ -28,12 +28,15 @@ DECLARE_FLAG_STRING(ebpf_converage_config_strategy); DECLARE_FLAG_STRING(ebpf_sample_config_strategy); DECLARE_FLAG_DOUBLE(ebpf_sample_config_config_rate); DECLARE_FLAG_BOOL(logtail_mode); +DECLARE_FLAG_STRING(host_path_blacklist); +DECLARE_FLAG_DOUBLE(default_machine_cpu_usage_threshold); namespace logtail { class AppConfigUnittest : public ::testing::Test { public: void TestRecurseParseJsonToFlags(); + void TestParseEnvToFlags(); private: void writeLogtailConfigJSON(const Json::Value& v) { @@ -168,7 +171,34 @@ void AppConfigUnittest::TestRecurseParseJsonToFlags() { APSARA_TEST_EQUAL(INT32_FLAG(ebpf_receive_event_chan_cap), 55); } +void AppConfigUnittest::TestParseEnvToFlags() { + // 忽略列表中的环境变量,继续可以用小写且允许 LOONG_ 前缀的格式 + { + SetEnv("host_path_blacklist", "test1"); + AppConfig::GetInstance()->ParseEnvToFlags(); + APSARA_TEST_EQUAL(STRING_FLAG(host_path_blacklist), "test1"); + UnsetEnv("host_path_blacklist"); + + SetEnv("LOONG_host_path_blacklist", "test2"); + AppConfig::GetInstance()->ParseEnvToFlags(); + APSARA_TEST_EQUAL(STRING_FLAG(host_path_blacklist), "test2"); + } + // 不忽略列表中的环境变量,需要为大写,LOONG_ 前缀 + { + SetEnv("default_machine_cpu_usage_threshold", "1"); + AppConfig::GetInstance()->ParseEnvToFlags(); + APSARA_TEST_NOT_EQUAL(DOUBLE_FLAG(default_machine_cpu_usage_threshold), 1); + APSARA_TEST_EQUAL(DOUBLE_FLAG(default_machine_cpu_usage_threshold), 0.4); + UnsetEnv("default_machine_cpu_usage_threshold"); + + SetEnv("LOONG_DEFAULT_MACHINE_CPU_USAGE_THRESHOLD", "2"); + AppConfig::GetInstance()->ParseEnvToFlags(); + APSARA_TEST_EQUAL(DOUBLE_FLAG(default_machine_cpu_usage_threshold), 2); + } +} + UNIT_TEST_CASE(AppConfigUnittest, TestRecurseParseJsonToFlags); +UNIT_TEST_CASE(AppConfigUnittest, TestParseEnvToFlags); } // namespace logtail diff --git a/docs/cn/installation/logtail-mode.md b/docs/cn/installation/logtail-mode.md index cf2fd4f0b2..12d9d78dd6 100644 --- a/docs/cn/installation/logtail-mode.md +++ b/docs/cn/installation/logtail-mode.md @@ -29,7 +29,7 @@ LoongCollector 提供了 Logtail 兼容模式,可以让您在升级到 LoongCo **方式二:环境变量** ```bash -export logtail_mode=true +export LOGTAIL_MODE=true ./loongcollector ``` @@ -42,7 +42,7 @@ export logtail_mode=true 1. 需要给LoongCollector容器添加环境变量: ```bash - logtail_mode=true + LOGTAIL_MODE=true ``` 2. 需要调整LoongCollector挂载路径映射: diff --git a/docs/cn/installation/loongcollector-dir.md b/docs/cn/installation/loongcollector-dir.md index e2580c5547..d5d1a86582 100644 --- a/docs/cn/installation/loongcollector-dir.md +++ b/docs/cn/installation/loongcollector-dir.md @@ -127,28 +127,28 @@ inotify日志:`/opt/loongcollector/run/inotify_watcher_dirs` LoongCollector 提供以下参数用于自定义各类目录位置: -- `loongcollector_conf_dir`: 配置目录 +- 配置目录: gflag为`conf_dir`、环境变量为`LOONG_CONF_DIR` -- `loongcollector_log_dir`: 日志目录 +- 日志目录: gflag为`logs_dir`、环境变量为`LOONG_LOGS_DIR` -- `loongcollector_data_dir`: 数据目录 +- 数据目录: gflag为`data_dir`、环境变量为`LOONG_DATA_DIR` -- `loongcollector_run_dir`: 运行时目录 +- 运行时目录: gflag为`run_dir`、环境变量为`LOONG_RUN_DIR` -- `loongcollector_third_party_dir`: 第三方依赖目录 +- 第三方依赖目录: gflag为`third_party_dir`、环境变量为`LOONG_THIRD_PARTY_DIR` ### 配置方式 1. 命令行参数: ```bash - ./loongcollector --loongcollector_conf_dir=/custom/path/conf + ./loongcollector --conf_dir=/custom/path/conf ``` 2. 环境变量: ```bash - export loongcollector_conf_dir=/custom/path/conf + export LOONG_CONF_DIR=/custom/path/conf ./loongcollector ``` diff --git a/pkg/flags/flags.go b/pkg/flags/flags.go index 2b7d47d43f..f41de5d203 100644 --- a/pkg/flags/flags.go +++ b/pkg/flags/flags.go @@ -18,7 +18,10 @@ import ( "context" "encoding/json" "flag" + "fmt" "os" + "strconv" + "strings" "sync" "github.com/alibaba/ilogtail/pkg/logger" @@ -32,9 +35,10 @@ const ( ) const ( - DefaultGlobalConfig = `{"InputIntervalMs":5000,"AggregatIntervalMs":30,"FlushIntervalMs":30,"DefaultLogQueueSize":11,"DefaultLogGroupQueueSize":12}` - DefaultPluginConfig = `{"inputs":[{"type":"metric_mock","detail":{"Tags":{"tag1":"aaaa","tag2":"bbb"},"Fields":{"content":"xxxxx","time":"2017.09.12 20:55:36"}}}],"flushers":[{"type":"flusher_stdout"}]}` - DefaultFlusherConfig = `{"type":"flusher_sls","detail":{}}` + DefaultGlobalConfig = `{"InputIntervalMs":5000,"AggregatIntervalMs":30,"FlushIntervalMs":30,"DefaultLogQueueSize":11,"DefaultLogGroupQueueSize":12}` + DefaultPluginConfig = `{"inputs":[{"type":"metric_mock","detail":{"Tags":{"tag1":"aaaa","tag2":"bbb"},"Fields":{"Content":"xxxxx","time":"2017.09.12 20:55:36"}}}],"flushers":[{"type":"flusher_stdout"}]}` + DefaultFlusherConfig = `{"type":"flusher_sls","detail":{}}` + LoongcollectorEnvPrefix = "LOONG_" ) var ( @@ -43,6 +47,25 @@ var ( flusherLoadOnce sync.Once ) +type LogType string + +const ( + LogTypeInfo LogType = "info" + LogTypeDebug LogType = "debug" + LogTypeWarning LogType = "warning" + LogTypeError LogType = "error" +) + +// LogInfo contains metadata about a log message +type LogInfo struct { + LogType LogType + Content string +} + +var ( + LogsWaitToPrint = []LogInfo{} +) + // flags used to control ilogtail. var ( K8sFlag = flag.Bool("ALICLOUD_LOG_K8S_FLAG", false, "alibaba log k8s event config flag, set true if you want to use it") @@ -120,6 +143,178 @@ var ( ClusterType = flag.String("GLOBAL_CLUSTER_TYPE", "", "cluster type, supporting ack, one, asi and k8s") ) +// lookupFlag returns the flag.Flag for the given name, or an error if not found +func lookupFlag(name string) (*flag.Flag, error) { + if f := flag.Lookup(name); f != nil { + return f, nil + } + return nil, fmt.Errorf("flag %s not found", name) +} + +// GetStringFlag returns the string value of the named flag +func GetStringFlag(name string) (string, error) { + f, err := lookupFlag(name) + if err != nil { + return "", err + } + return f.Value.String(), nil +} + +// GetBoolFlag returns the bool value of the named flag +func GetBoolFlag(name string) (bool, error) { + f, err := lookupFlag(name) + if err != nil { + return false, err + } + + if v, ok := f.Value.(flag.Getter); ok { + if val, ok := v.Get().(bool); ok { + return val, nil + } + } + return false, fmt.Errorf("flag %s is not bool type", name) +} + +// GetIntFlag returns the int value of the named flag +func GetIntFlag(name string) (int, error) { + f, err := lookupFlag(name) + if err != nil { + return 0, err + } + + if v, ok := f.Value.(flag.Getter); ok { + if val, ok := v.Get().(int); ok { + return val, nil + } + } + return 0, fmt.Errorf("flag %s is not int type", name) +} + +// GetFloat64Flag returns the float64 value of the named flag +func GetFloat64Flag(name string) (float64, error) { + f, err := lookupFlag(name) + if err != nil { + return 0.0, err + } + + if v, ok := f.Value.(flag.Getter); ok { + if val, ok := v.Get().(float64); ok { + return val, nil + } + } + return 0.0, fmt.Errorf("flag %s is not float64 type", name) +} + +// SetStringFlag sets the string value of the named flag +func SetStringFlag(name, value string) error { + f, err := lookupFlag(name) + if err != nil { + return err + } + return f.Value.Set(value) +} + +// SetBoolFlag sets the bool value of the named flag +func SetBoolFlag(name string, value bool) error { + f, err := lookupFlag(name) + if err != nil { + return err + } + return f.Value.Set(strconv.FormatBool(value)) +} + +// SetIntFlag sets the int value of the named flag +func SetIntFlag(name string, value int) error { + f, err := lookupFlag(name) + if err != nil { + return err + } + return f.Value.Set(strconv.Itoa(value)) +} + +// SetFloat64Flag sets the float64 value of the named flag +func SetFloat64Flag(name string, value float64) error { + f, err := lookupFlag(name) + if err != nil { + return err + } + return f.Value.Set(strconv.FormatFloat(value, 'g', -1, 64)) +} + +// LoadEnvToFlags loads environment variables into flags +func LoadEnvToFlags() { + for _, env := range os.Environ() { + name, value, found := strings.Cut(env, "=") + if !found { + continue + } + + if !strings.HasPrefix(name, LoongcollectorEnvPrefix) { + continue + } + + flagName := strings.ToLower(strings.TrimPrefix(name, LoongcollectorEnvPrefix)) + f := flag.Lookup(flagName) + if f == nil { + continue + } + + oldValue := f.Value.String() + getter, ok := f.Value.(flag.Getter) + if !ok { + LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{ + LogType: LogTypeError, + Content: fmt.Sprintf("Flag does not support Get operation, flag: %s, value: %s", flagName, oldValue), + }) + continue + } + + actualValue := getter.Get() + var err error + + // Validate value type before setting + switch actualValue.(type) { + case bool: + _, err = strconv.ParseBool(value) + case int, int64: + _, err = strconv.ParseInt(value, 10, 64) + case uint, uint64: + _, err = strconv.ParseUint(value, 10, 64) + case float64: + _, err = strconv.ParseFloat(value, 64) + case string: + // No validation needed + default: + LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{ + LogType: LogTypeError, + Content: fmt.Sprintf("Unsupported flag type: %s (%T)", flagName, actualValue), + }) + continue + } + + if err != nil { + LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{ + LogType: LogTypeError, + Content: fmt.Sprintf("Invalid value for flag %s (%T): %s - %v", flagName, actualValue, value, err), + }) + continue + } + + if err := f.Value.Set(value); err != nil { + LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{ + LogType: LogTypeError, + Content: fmt.Sprintf("Failed to set flag %s: %v (old: %s, new: %s)", flagName, err, oldValue, value), + }) + continue + } + + LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{ + LogType: LogTypeInfo, + Content: fmt.Sprintf("Updated flag %s (%T): %s -> %s", flagName, actualValue, oldValue, f.Value.String()), + }) + } +} + func init() { _ = util.InitFromEnvBool("ALICLOUD_LOG_K8S_FLAG", K8sFlag, *K8sFlag) _ = util.InitFromEnvBool("ALICLOUD_LOG_DOCKER_ENV_CONFIG", DockerConfigInitFlag, *DockerConfigInitFlag) @@ -149,7 +344,10 @@ func init() { if len(*DefaultRegion) == 0 { *DefaultRegion = util.GuessRegionByEndpoint(*LogServiceEndpoint, "cn-hangzhou") - logger.Info(context.Background(), "guess region by endpoint, endpoint", *LogServiceEndpoint, "region", *DefaultRegion) + LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{ + LogType: LogTypeInfo, + Content: fmt.Sprintf("guess region by endpoint, endpoint: %s, region: %s", *LogServiceEndpoint, *DefaultRegion), + }) } _ = util.InitFromEnvInt("ALICLOUD_LOG_ENV_CONFIG_UPDATE_INTERVAL", DockerEnvUpdateInterval, *DockerEnvUpdateInterval) @@ -157,6 +355,8 @@ func init() { if *DockerConfigInitFlag && *DockerConfigPluginInitFlag { _ = util.InitFromEnvBool("ALICLOUD_LOG_DOCKER_ENV_CONFIG_SELF", &SelfEnvConfigFlag, false) } + // 最后执行,优先级最高 + LoadEnvToFlags() } // GetFlusherConfiguration returns the flusher category and options. diff --git a/pkg/flags/flags_test.go b/pkg/flags/flags_test.go new file mode 100644 index 0000000000..400b014ade --- /dev/null +++ b/pkg/flags/flags_test.go @@ -0,0 +1,153 @@ +// Copyright 2021 iLogtail Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package flags + +import ( + "flag" + "os" + "testing" +) + +func TestLoadEnvToFlags(t *testing.T) { + tests := []struct { + name string + envs map[string]string + flags map[string]interface{} + expected map[string]interface{} + }{ + { + name: "string flag", + envs: map[string]string{ + LoongcollectorEnvPrefix + "CONFIG": "/etc/ilogtail/config.json", + }, + flags: map[string]interface{}{ + "config": "", + }, + expected: map[string]interface{}{ + "config": "/etc/ilogtail/config.json", + }, + }, + { + name: "bool flag", + envs: map[string]string{ + LoongcollectorEnvPrefix + "DEBUG": "true", + }, + flags: map[string]interface{}{ + "debug": false, + }, + expected: map[string]interface{}{ + "debug": true, + }, + }, + { + name: "int flag", + envs: map[string]string{ + LoongcollectorEnvPrefix + "PORT": "8080", + }, + flags: map[string]interface{}{ + "port": 0, + }, + expected: map[string]interface{}{ + "port": 8080, + }, + }, + { + name: "env not exist", + envs: map[string]string{}, + flags: map[string]interface{}{ + "config": "default", + }, + expected: map[string]interface{}{ + "config": "default", + }, + }, + { + name: "invalid bool value", + envs: map[string]string{ + LoongcollectorEnvPrefix + "DEBUG": "invalid", + }, + flags: map[string]interface{}{ + "debug": false, + }, + expected: map[string]interface{}{ + "debug": false, + }, + }, + { + name: "invalid int value", + envs: map[string]string{ + LoongcollectorEnvPrefix + "PORT": "invalid", + }, + flags: map[string]interface{}{ + "port": 0, + }, + expected: map[string]interface{}{ + "port": 0, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Clean environment variables + os.Clearenv() + + // Set environment variables + for k, v := range tt.envs { + os.Setenv(k, v) + } + + // Create flags + flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) + for name, value := range tt.flags { + switch v := value.(type) { + case string: + flag.String(name, v, "test flag") + case bool: + flag.Bool(name, v, "test flag") + case int: + flag.Int(name, v, "test flag") + } + } + + // Load environment variables to flags + LoadEnvToFlags() + + found := false + // Verify flag values + flag.VisitAll(func(f *flag.Flag) { + found = true + expected := tt.expected[f.Name] + switch v := expected.(type) { + case string: + if f.Value.String() != v { + t.Errorf("flag %s = %s, want %s", f.Name, f.Value.String(), v) + } + case bool: + if f.Value.String() != "true" && v || f.Value.String() != "false" && !v { + t.Errorf("flag %s = %s, want %v", f.Name, f.Value.String(), v) + } + case int: + if f.Value.String() != "0" && v == 0 || f.Value.String() != "8080" && v == 8080 { + t.Errorf("flag %s = %s, want %d", f.Name, f.Value.String(), v) + } + } + }) + if !found { + t.Errorf("flag %s not found", tt) + } + }) + } +} diff --git a/plugin_main/plugin_export.go b/plugin_main/plugin_export.go index 392d947081..9a2650c7ca 100644 --- a/plugin_main/plugin_export.go +++ b/plugin_main/plugin_export.go @@ -116,6 +116,18 @@ func LoadGlobalConfig(jsonStr string) int { retcode = 1 } logger.InitLogger() + for _, log := range flags.LogsWaitToPrint { + switch log.LogType { + case flags.LogTypeError: + logger.Error(context.Background(), log.Content) + case flags.LogTypeInfo: + logger.Info(context.Background(), log.Content) + case flags.LogTypeDebug: + logger.Debug(context.Background(), log.Content) + case flags.LogTypeWarning: + logger.Warning(context.Background(), log.Content) + } + } logger.Info(context.Background(), "load global config", jsonStr) config.UserAgent = fmt.Sprintf("ilogtail/%v (%v) ip/%v", config.BaseVersion, runtime.GOOS, config.LoongcollectorGlobalConfig.HostIP) }