github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/flags/flags.go (about)

     1  // Copyright 2021 iLogtail Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package flags
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"flag"
    21  	"fmt"
    22  	"os"
    23  	"strconv"
    24  	"strings"
    25  	"sync"
    26  
    27  	"github.com/alibaba/ilogtail/pkg/logger"
    28  	"github.com/alibaba/ilogtail/pkg/util"
    29  )
    30  
    31  const (
    32  	DeployDaemonset   = "daemonset"
    33  	DeployStatefulSet = "statefulset"
    34  	DeploySingleton   = "singleton"
    35  )
    36  
    37  const (
    38  	DefaultGlobalConfig     = `{"InputIntervalMs":5000,"AggregatIntervalMs":30,"FlushIntervalMs":30,"DefaultLogQueueSize":11,"DefaultLogGroupQueueSize":12}`
    39  	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"}]}`
    40  	DefaultFlusherConfig    = `{"type":"flusher_sls","detail":{}}`
    41  	LoongcollectorEnvPrefix = "LOONG_"
    42  )
    43  
    44  var (
    45  	flusherType     string
    46  	flusherCfg      map[string]interface{}
    47  	flusherLoadOnce sync.Once
    48  )
    49  
    50  type LogType string
    51  
    52  const (
    53  	LogTypeInfo    LogType = "info"
    54  	LogTypeDebug   LogType = "debug"
    55  	LogTypeWarning LogType = "warning"
    56  	LogTypeError   LogType = "error"
    57  )
    58  
    59  // LogInfo contains metadata about a log message
    60  type LogInfo struct {
    61  	LogType LogType
    62  	Content string
    63  }
    64  
    65  var (
    66  	LogsWaitToPrint = []LogInfo{}
    67  )
    68  
    69  // flags used to control ilogtail.
    70  var (
    71  	K8sFlag = flag.Bool("ALICLOUD_LOG_K8S_FLAG", false, "alibaba log k8s event config flag, set true if you want to use it")
    72  	// DockerConfigInitFlag is the alibaba log docker env config flag, set yes if you want to use it. And it is also a special flag to control enable go part in ilogtail. If you just want to
    73  	// enable logtail plugin and off the env config, set the env called ALICLOUD_LOG_PLUGIN_ENV_CONFIG with false.
    74  	DockerConfigInitFlag       = flag.Bool("ALICLOUD_LOG_DOCKER_ENV_CONFIG", false, "alibaba log docker env config flag, set true if you want to use it")
    75  	DockerConfigPluginInitFlag = flag.Bool("ALICLOUD_LOG_PLUGIN_ENV_CONFIG", true, "alibaba log docker env config flag, set true if you want to use it")
    76  	// AliCloudECSFlag set true if your docker is on alicloud ECS, so we can use ECS meta
    77  	AliCloudECSFlag = flag.Bool("ALICLOUD_LOG_ECS_FLAG", false, "set true if your docker is on alicloud ECS, so we can use ECS meta")
    78  
    79  	// DockerConfigPrefix docker env config prefix
    80  	DockerConfigPrefix = flag.String("ALICLOUD_LOG_DOCKER_CONFIG_PREFIX", "aliyun_logs_", "docker env config prefix")
    81  
    82  	// LogServiceEndpoint default project to create config
    83  	// https://www.alibabacloud.com/help/doc-detail/29008.htm
    84  	LogServiceEndpoint = flag.String("ALICLOUD_LOG_ENDPOINT", "cn-hangzhou.log.aliyuncs.com", "log service endpoint of your project's region")
    85  
    86  	// DefaultLogProject default project to create config
    87  	DefaultLogProject = flag.String("ALICLOUD_LOG_DEFAULT_PROJECT", "", "default project to create config")
    88  
    89  	// DefaultLogMachineGroup default project to create config
    90  	DefaultLogMachineGroup = flag.String("ALICLOUD_LOG_DEFAULT_MACHINE_GROUP", "", "default project to create config")
    91  
    92  	// LogResourceCacheExpireSec log service's resources cache expire seconds
    93  	LogResourceCacheExpireSec = flag.Int("ALICLOUD_LOG_CACHE_EXPIRE_SEC", 600, "log service's resources cache expire seconds")
    94  
    95  	// LogOperationMaxRetryTimes log service's operation max retry times
    96  	LogOperationMaxRetryTimes = flag.Int("ALICLOUD_LOG_OPERATION_MAX_TRY", 3, "log service's operation max retry times")
    97  
    98  	// DefaultAccessKeyID your log service's access key id
    99  	DefaultAccessKeyID = flag.String("ALICLOUD_LOG_ACCESS_KEY_ID", "xxxxxxxxx", "your log service's access key id")
   100  
   101  	// DefaultAccessKeySecret your log service's access key secret
   102  	DefaultAccessKeySecret = flag.String("ALICLOUD_LOG_ACCESS_KEY_SECRET", "xxxxxxxxx", "your log service's access key secret")
   103  
   104  	// DefaultSTSToken your sts token
   105  	DefaultSTSToken = flag.String("ALICLOUD_LOG_STS_TOKEN", "", "set sts token if you use sts")
   106  
   107  	// LogConfigPrefix config prefix
   108  	LogConfigPrefix = flag.String("ALICLOUD_LOG_CONFIG_PREFIX", "aliyun_logs_", "config prefix")
   109  
   110  	// DockerEnvUpdateInterval docker env config update interval seconds
   111  	DockerEnvUpdateInterval = flag.Int("ALICLOUD_LOG_ENV_CONFIG_UPDATE_INTERVAL", 10, "docker env config update interval seconds")
   112  
   113  	// ProductAPIDomain product domain
   114  	ProductAPIDomain = flag.String("ALICLOUD_LOG_PRODUCT_DOMAIN", "sls.aliyuncs.com", "product domain config")
   115  
   116  	// DefaultRegion default log region"
   117  	DefaultRegion = flag.String("ALICLOUD_LOG_REGION", "", "default log region")
   118  
   119  	SelfEnvConfigFlag bool
   120  
   121  	EnableContainerdUpperDirDetect = flag.Bool("enable_containerd_upper_dir_detect", false, "if enable containerd upper dir detect when locating rootfs")
   122  	GlobalConfig                   = flag.String("global", "./global.json", "global config.")
   123  	PluginConfig                   = flag.String("plugin", "./plugin.json", "plugin config.")
   124  	FlusherConfig                  = flag.String("flusher", "./default_flusher.json", "the default flusher configuration is used not only in the plugins without flusher but also to transfer the self telemetry data.")
   125  	ForceSelfCollect               = flag.Bool("force-statics", false, "force collect self telemetry data before closing.")
   126  	AutoProfile                    = flag.Bool("prof-auto", true, "auto dump prof file when prof-flag is open.")
   127  	HTTPProfFlag                   = flag.Bool("prof-flag", false, "http pprof flag.")
   128  	Cpuprofile                     = flag.String("cpu-profile", "cpu.prof", "write cpu profile to file.")
   129  	Memprofile                     = flag.String("mem-profile", "mem.prof", "write mem profile to file.")
   130  	HTTPAddr                       = flag.String("server", ":18689", "http server address.")
   131  	Doc                            = flag.Bool("doc", false, "generate plugin docs")
   132  	DocPath                        = flag.String("docpath", "./docs/en/plugins", "generate plugin docs")
   133  	HTTPLoadFlag                   = flag.Bool("http-load", false, "export http endpoint for load plugin config.")
   134  	FileIOFlag                     = flag.Bool("file-io", false, "use file for input or output.")
   135  	InputFile                      = flag.String("input-file", "./input.log", "input file")
   136  	InputField                     = flag.String("input-field", "content", "input file")
   137  	InputLineLimit                 = flag.Int("input-line-limit", 1000, "input file")
   138  	OutputFile                     = flag.String("output-file", "./output.log", "output file")
   139  	StatefulSetFlag                = flag.Bool("ALICLOUD_LOG_STATEFULSET_FLAG", false, "alibaba log export ports flag, set true if you want to use it")
   140  
   141  	DeployMode           = flag.String("DEPLOY_MODE", DeployDaemonset, "alibaba log deploy mode, daemonset or statefulset or singleton")
   142  	EnableKubernetesMeta = flag.Bool("ENABLE_KUBERNETES_META", false, "enable kubernetes meta")
   143  	ClusterID            = flag.String("GLOBAL_CLUSTER_ID", "", "cluster id")
   144  	ClusterType          = flag.String("GLOBAL_CLUSTER_TYPE", "", "cluster type, supporting ack, one, asi and k8s")
   145  )
   146  
   147  // lookupFlag returns the flag.Flag for the given name, or an error if not found
   148  func lookupFlag(name string) (*flag.Flag, error) {
   149  	if f := flag.Lookup(name); f != nil {
   150  		return f, nil
   151  	}
   152  	return nil, fmt.Errorf("flag %s not found", name)
   153  }
   154  
   155  // GetStringFlag returns the string value of the named flag
   156  func GetStringFlag(name string) (string, error) {
   157  	f, err := lookupFlag(name)
   158  	if err != nil {
   159  		return "", err
   160  	}
   161  	return f.Value.String(), nil
   162  }
   163  
   164  // GetBoolFlag returns the bool value of the named flag
   165  func GetBoolFlag(name string) (bool, error) {
   166  	f, err := lookupFlag(name)
   167  	if err != nil {
   168  		return false, err
   169  	}
   170  
   171  	if v, ok := f.Value.(flag.Getter); ok {
   172  		if val, ok := v.Get().(bool); ok {
   173  			return val, nil
   174  		}
   175  	}
   176  	return false, fmt.Errorf("flag %s is not bool type", name)
   177  }
   178  
   179  // GetIntFlag returns the int value of the named flag
   180  func GetIntFlag(name string) (int, error) {
   181  	f, err := lookupFlag(name)
   182  	if err != nil {
   183  		return 0, err
   184  	}
   185  
   186  	if v, ok := f.Value.(flag.Getter); ok {
   187  		if val, ok := v.Get().(int); ok {
   188  			return val, nil
   189  		}
   190  	}
   191  	return 0, fmt.Errorf("flag %s is not int type", name)
   192  }
   193  
   194  // GetFloat64Flag returns the float64 value of the named flag
   195  func GetFloat64Flag(name string) (float64, error) {
   196  	f, err := lookupFlag(name)
   197  	if err != nil {
   198  		return 0.0, err
   199  	}
   200  
   201  	if v, ok := f.Value.(flag.Getter); ok {
   202  		if val, ok := v.Get().(float64); ok {
   203  			return val, nil
   204  		}
   205  	}
   206  	return 0.0, fmt.Errorf("flag %s is not float64 type", name)
   207  }
   208  
   209  // SetStringFlag sets the string value of the named flag
   210  func SetStringFlag(name, value string) error {
   211  	f, err := lookupFlag(name)
   212  	if err != nil {
   213  		return err
   214  	}
   215  	return f.Value.Set(value)
   216  }
   217  
   218  // SetBoolFlag sets the bool value of the named flag
   219  func SetBoolFlag(name string, value bool) error {
   220  	f, err := lookupFlag(name)
   221  	if err != nil {
   222  		return err
   223  	}
   224  	return f.Value.Set(strconv.FormatBool(value))
   225  }
   226  
   227  // SetIntFlag sets the int value of the named flag
   228  func SetIntFlag(name string, value int) error {
   229  	f, err := lookupFlag(name)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	return f.Value.Set(strconv.Itoa(value))
   234  }
   235  
   236  // SetFloat64Flag sets the float64 value of the named flag
   237  func SetFloat64Flag(name string, value float64) error {
   238  	f, err := lookupFlag(name)
   239  	if err != nil {
   240  		return err
   241  	}
   242  	return f.Value.Set(strconv.FormatFloat(value, 'g', -1, 64))
   243  }
   244  
   245  // LoadEnvToFlags loads environment variables into flags
   246  func LoadEnvToFlags() {
   247  	for _, env := range os.Environ() {
   248  		name, value, found := strings.Cut(env, "=")
   249  		if !found {
   250  			continue
   251  		}
   252  		var flagName string
   253  		if strings.HasPrefix(name, LoongcollectorEnvPrefix) {
   254  			flagName = strings.ToLower(strings.TrimPrefix(name, LoongcollectorEnvPrefix))
   255  		} else {
   256  			flagName = name
   257  		}
   258  
   259  		f := flag.Lookup(flagName)
   260  		if f == nil {
   261  			continue
   262  		}
   263  
   264  		oldValue := f.Value.String()
   265  		getter, ok := f.Value.(flag.Getter)
   266  		if !ok {
   267  			LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{
   268  				LogType: LogTypeError,
   269  				Content: fmt.Sprintf("Flag does not support Get operation, flag: %s, value: %s", flagName, oldValue),
   270  			})
   271  			continue
   272  		}
   273  
   274  		actualValue := getter.Get()
   275  		var err error
   276  
   277  		// Validate value type before setting
   278  		switch actualValue.(type) {
   279  		case bool:
   280  			_, err = strconv.ParseBool(value)
   281  		case int, int64:
   282  			_, err = strconv.ParseInt(value, 10, 64)
   283  		case uint, uint64:
   284  			_, err = strconv.ParseUint(value, 10, 64)
   285  		case float64:
   286  			_, err = strconv.ParseFloat(value, 64)
   287  		case string:
   288  			// No validation needed
   289  		default:
   290  			LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{
   291  				LogType: LogTypeError,
   292  				Content: fmt.Sprintf("Unsupported flag type: %s (%T)", flagName, actualValue),
   293  			})
   294  			continue
   295  		}
   296  
   297  		if err != nil {
   298  			LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{
   299  				LogType: LogTypeError,
   300  				Content: fmt.Sprintf("Invalid value for flag %s (%T): %s - %v", flagName, actualValue, value, err),
   301  			})
   302  			continue
   303  		}
   304  
   305  		if err := f.Value.Set(value); err != nil {
   306  			LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{
   307  				LogType: LogTypeError,
   308  				Content: fmt.Sprintf("Failed to set flag %s: %v (old: %s, new: %s)", flagName, err, oldValue, value),
   309  			})
   310  			continue
   311  		}
   312  
   313  		LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{
   314  			LogType: LogTypeInfo,
   315  			Content: fmt.Sprintf("Updated flag %s (%T): %s -> %s", flagName, actualValue, oldValue, f.Value.String()),
   316  		})
   317  	}
   318  }
   319  
   320  func init() {
   321  	_ = util.InitFromEnvBool("ALICLOUD_LOG_K8S_FLAG", K8sFlag, *K8sFlag)
   322  	_ = util.InitFromEnvBool("ALICLOUD_LOG_DOCKER_ENV_CONFIG", DockerConfigInitFlag, *DockerConfigInitFlag)
   323  	_ = util.InitFromEnvBool("ALICLOUD_LOG_ECS_FLAG", AliCloudECSFlag, *AliCloudECSFlag)
   324  	_ = util.InitFromEnvString("ALICLOUD_LOG_DOCKER_CONFIG_PREFIX", DockerConfigPrefix, *DockerConfigPrefix)
   325  	_ = util.InitFromEnvString("ALICLOUD_LOG_DEFAULT_PROJECT", DefaultLogProject, *DefaultLogProject)
   326  	_ = util.InitFromEnvString("ALICLOUD_LOG_DEFAULT_MACHINE_GROUP", DefaultLogMachineGroup, *DefaultLogMachineGroup)
   327  	_ = util.InitFromEnvString("ALICLOUD_LOG_ENDPOINT", LogServiceEndpoint, *LogServiceEndpoint)
   328  	_ = util.InitFromEnvString("ALICLOUD_LOG_ACCESS_KEY_ID", DefaultAccessKeyID, *DefaultAccessKeyID)
   329  	_ = util.InitFromEnvString("ALICLOUD_LOG_ACCESS_KEY_SECRET", DefaultAccessKeySecret, *DefaultAccessKeySecret)
   330  	_ = util.InitFromEnvString("ALICLOUD_LOG_STS_TOKEN", DefaultSTSToken, *DefaultSTSToken)
   331  	_ = util.InitFromEnvString("ALICLOUD_LOG_CONFIG_PREFIX", LogConfigPrefix, *LogConfigPrefix)
   332  	_ = util.InitFromEnvString("ALICLOUD_LOG_PRODUCT_DOMAIN", ProductAPIDomain, *ProductAPIDomain)
   333  	_ = util.InitFromEnvString("ALICLOUD_LOG_REGION", DefaultRegion, *DefaultRegion)
   334  	_ = util.InitFromEnvBool("ALICLOUD_LOG_PLUGIN_ENV_CONFIG", DockerConfigPluginInitFlag, *DockerConfigPluginInitFlag)
   335  
   336  	_ = util.InitFromEnvBool("enable_containerd_upper_dir_detect", EnableContainerdUpperDirDetect, *EnableContainerdUpperDirDetect)
   337  	_ = util.InitFromEnvBool("LOGTAIL_DEBUG_FLAG", HTTPProfFlag, *HTTPProfFlag)
   338  	_ = util.InitFromEnvBool("LOGTAIL_AUTO_PROF", AutoProfile, *AutoProfile)
   339  	_ = util.InitFromEnvBool("LOGTAIL_FORCE_COLLECT_SELF_TELEMETRY", ForceSelfCollect, *ForceSelfCollect)
   340  	_ = util.InitFromEnvBool("LOGTAIL_HTTP_LOAD_CONFIG", HTTPLoadFlag, *HTTPLoadFlag)
   341  	_ = util.InitFromEnvBool("ALICLOUD_LOG_STATEFULSET_FLAG", StatefulSetFlag, *StatefulSetFlag)
   342  
   343  	_ = util.InitFromEnvString("DEPLOY_MODE", DeployMode, *DeployMode)
   344  	_ = util.InitFromEnvBool("ENABLE_KUBERNETES_META", EnableKubernetesMeta, *EnableKubernetesMeta)
   345  	_ = util.InitFromEnvString("GLOBAL_CLUSTER_ID", ClusterID, *ClusterID)
   346  	_ = util.InitFromEnvString("GLOBAL_CLUSTER_TYPE", ClusterType, *ClusterType)
   347  
   348  	if len(*DefaultRegion) == 0 {
   349  		*DefaultRegion = util.GuessRegionByEndpoint(*LogServiceEndpoint, "cn-hangzhou")
   350  		LogsWaitToPrint = append(LogsWaitToPrint, LogInfo{
   351  			LogType: LogTypeInfo,
   352  			Content: fmt.Sprintf("guess region by endpoint, endpoint: %s, region: %s", *LogServiceEndpoint, *DefaultRegion),
   353  		})
   354  	}
   355  
   356  	_ = util.InitFromEnvInt("ALICLOUD_LOG_ENV_CONFIG_UPDATE_INTERVAL", DockerEnvUpdateInterval, *DockerEnvUpdateInterval)
   357  
   358  	if *DockerConfigInitFlag && *DockerConfigPluginInitFlag {
   359  		_ = util.InitFromEnvBool("ALICLOUD_LOG_DOCKER_ENV_CONFIG_SELF", &SelfEnvConfigFlag, false)
   360  	}
   361  	// 最后执行,优先级最高
   362  	LoadEnvToFlags()
   363  }
   364  
   365  // GetFlusherConfiguration returns the flusher category and options.
   366  func GetFlusherConfiguration() (flusherCategory string, flusherOptions map[string]interface{}) {
   367  	flusherLoadOnce.Do(func() {
   368  		extract := func(cfg []byte) (string, map[string]interface{}, bool) {
   369  			m := make(map[string]interface{})
   370  			err := json.Unmarshal(cfg, &m)
   371  			if err != nil {
   372  				logger.Error(context.Background(), "DEFAULT_FLUSHER_ALARM", "err", err)
   373  				return "", nil, false
   374  			}
   375  			c, ok := m["type"].(string)
   376  			if !ok {
   377  				return "", nil, false
   378  			}
   379  			options, ok := m["detail"].(map[string]interface{})
   380  			if !ok {
   381  				return c, nil, true
   382  			}
   383  			return c, options, true
   384  		}
   385  		if fCfg, err := os.ReadFile(*FlusherConfig); err == nil {
   386  			category, options, ok := extract(fCfg)
   387  			if ok {
   388  				flusherType = category
   389  				flusherCfg = options
   390  			} else {
   391  				flusherType, flusherCfg, _ = extract([]byte(DefaultFlusherConfig))
   392  			}
   393  		} else {
   394  			flusherType, flusherCfg, _ = extract([]byte(DefaultFlusherConfig))
   395  		}
   396  
   397  	})
   398  	return flusherType, flusherCfg
   399  }