| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package command |
| |
| import ( |
| "flag" |
| "os" |
| "strconv" |
| |
| "github.com/hashicorp/vault/internalshared/configutil" |
| "github.com/posener/complete" |
| ) |
| |
| // logFlags are the 'log' related flags that can be shared across commands. |
| type logFlags struct { |
| flagCombineLogs bool |
| flagLogLevel string |
| flagLogFormat string |
| flagLogFile string |
| flagLogRotateBytes int |
| flagLogRotateDuration string |
| flagLogRotateMaxFiles int |
| } |
| |
| // valuesProvider has the intention of providing a way to supply a func with a |
| // way to retrieve values for flags and environment variables without having to |
| // directly call a specific implementation. |
| // The reasoning for its existence is to facilitate testing. |
| type valuesProvider struct { |
| flagProvider func(string) (flag.Value, bool) |
| envVarProvider func(string) (string, bool) |
| } |
| |
| // addLogFlags will add the set of 'log' related flags to a flag set. |
| func (f *FlagSet) addLogFlags(l *logFlags) { |
| f.BoolVar(&BoolVar{ |
| Name: flagNameCombineLogs, |
| Target: &l.flagCombineLogs, |
| Default: false, |
| Hidden: true, |
| }) |
| |
| f.StringVar(&StringVar{ |
| Name: flagNameLogLevel, |
| Target: &l.flagLogLevel, |
| Default: notSetValue, |
| EnvVar: EnvVaultLogLevel, |
| Completion: complete.PredictSet("trace", "debug", "info", "warn", "error"), |
| Usage: "Log verbosity level. Supported values (in order of detail) are " + |
| "\"trace\", \"debug\", \"info\", \"warn\", and \"error\".", |
| }) |
| |
| f.StringVar(&StringVar{ |
| Name: flagNameLogFormat, |
| Target: &l.flagLogFormat, |
| Default: notSetValue, |
| EnvVar: EnvVaultLogFormat, |
| Completion: complete.PredictSet("standard", "json"), |
| Usage: `Log format. Supported values are "standard" and "json".`, |
| }) |
| |
| f.StringVar(&StringVar{ |
| Name: flagNameLogFile, |
| Target: &l.flagLogFile, |
| Usage: "Path to the log file that Vault should use for logging", |
| }) |
| |
| f.IntVar(&IntVar{ |
| Name: flagNameLogRotateBytes, |
| Target: &l.flagLogRotateBytes, |
| Usage: "Number of bytes that should be written to a log before it needs to be rotated. " + |
| "Unless specified, there is no limit to the number of bytes that can be written to a log file", |
| }) |
| |
| f.StringVar(&StringVar{ |
| Name: flagNameLogRotateDuration, |
| Target: &l.flagLogRotateDuration, |
| Usage: "The maximum duration a log should be written to before it needs to be rotated. " + |
| "Must be a duration value such as 30s", |
| }) |
| |
| f.IntVar(&IntVar{ |
| Name: flagNameLogRotateMaxFiles, |
| Target: &l.flagLogRotateMaxFiles, |
| Usage: "The maximum number of older log file archives to keep", |
| }) |
| } |
| |
| // envVarValue attempts to get a named value from the environment variables. |
| // The value will be returned as a string along with a boolean value indiciating |
| // to the caller whether the named env var existed. |
| func envVarValue(key string) (string, bool) { |
| if key == "" { |
| return "", false |
| } |
| return os.LookupEnv(key) |
| } |
| |
| // flagValue attempts to find the named flag in a set of FlagSets. |
| // The flag.Value is returned if it was specified, and the boolean value indicates |
| // to the caller if the flag was specified by the end user. |
| func (f *FlagSets) flagValue(flagName string) (flag.Value, bool) { |
| var result flag.Value |
| var isFlagSpecified bool |
| |
| if f != nil { |
| f.Visit(func(fl *flag.Flag) { |
| if fl.Name == flagName { |
| result = fl.Value |
| isFlagSpecified = true |
| } |
| }) |
| } |
| |
| return result, isFlagSpecified |
| } |
| |
| // overrideValue uses the provided keys to check CLI flags and environment |
| // variables for values that may be used to override any specified configuration. |
| func (p *valuesProvider) overrideValue(flagKey, envVarKey string) (string, bool) { |
| var result string |
| found := true |
| |
| flg, flgFound := p.flagProvider(flagKey) |
| env, envFound := p.envVarProvider(envVarKey) |
| |
| switch { |
| case flgFound: |
| result = flg.String() |
| case envFound: |
| result = env |
| default: |
| found = false |
| } |
| |
| return result, found |
| } |
| |
| // applyLogConfigOverrides will accept a shared config and specifically attempt to update the 'log' related config keys. |
| // For each 'log' key, we aggregate file config, env vars and CLI flags to select the one with the highest precedence. |
| // This method mutates the config object passed into it. |
| func (f *FlagSets) applyLogConfigOverrides(config *configutil.SharedConfig) { |
| p := &valuesProvider{ |
| flagProvider: f.flagValue, |
| envVarProvider: envVarValue, |
| } |
| |
| // Update log level |
| if val, found := p.overrideValue(flagNameLogLevel, EnvVaultLogLevel); found { |
| config.LogLevel = val |
| } |
| |
| // Update log format |
| if val, found := p.overrideValue(flagNameLogFormat, EnvVaultLogFormat); found { |
| config.LogFormat = val |
| } |
| |
| // Update log file name |
| if val, found := p.overrideValue(flagNameLogFile, ""); found { |
| config.LogFile = val |
| } |
| |
| // Update log rotation duration |
| if val, found := p.overrideValue(flagNameLogRotateDuration, ""); found { |
| config.LogRotateDuration = val |
| } |
| |
| // Update log max files |
| if val, found := p.overrideValue(flagNameLogRotateMaxFiles, ""); found { |
| config.LogRotateMaxFiles, _ = strconv.Atoi(val) |
| } |
| |
| // Update log rotation max bytes |
| if val, found := p.overrideValue(flagNameLogRotateBytes, ""); found { |
| config.LogRotateBytes, _ = strconv.Atoi(val) |
| } |
| } |