open-vault/command/log_flags.go
Peter Wilson 33e6a3a87c
VAULT-9900: Log rotation for 'agent' and 'server' commands (#18031)
* Work to unify log-file for agent/server and add rotation
* Updates to rotation code, tried to centralise the log config setup
* logging + tests
* Move LogFile to ShareConfig in test
* Docs
2022-11-29 14:07:04 +00:00

155 lines
4.9 KiB
Go

package command
import (
"flag"
"os"
"strings"
"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 string
flagLogRotateDuration string
flagLogRotateMaxFiles string
}
type provider = func(key string) (string, bool)
// 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 provider
envVarProvider provider
}
// 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.StringVar(&StringVar{
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.StringVar(&StringVar{
Name: flagNameLogRotateMaxFiles,
Target: &l.flagLogRotateMaxFiles,
Usage: "The maximum number of older log file archives to keep",
})
}
// getValue will attempt to find the flag with the corresponding flag name (key)
// and return the value along with a bool representing whether of not the flag had been found/set.
func (f *FlagSets) getValue(flagName string) (string, bool) {
var result string
var isFlagSpecified bool
if f != nil {
f.Visit(func(fl *flag.Flag) {
if fl.Name == flagName {
result = fl.Value.String()
isFlagSpecified = true
}
})
}
return result, isFlagSpecified
}
// getAggregatedConfigValue uses the provided keys to check CLI flags and environment
// variables for values that may be used to override any specified configuration.
// If nothing can be found in flags/env vars or config, the 'fallback' (default) value will be provided.
func (p *valuesProvider) getAggregatedConfigValue(flagKey, envVarKey, current, fallback string) string {
var result string
current = strings.TrimSpace(current)
flg, flgFound := p.flagProvider(flagKey)
env, envFound := p.envVarProvider(envVarKey)
switch {
case flgFound:
result = flg
case envFound:
// Use value from env var
result = env
case current != "":
// Use value from config
result = current
default:
// Use the default value
result = fallback
}
return result
}
// updateLogConfig 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) updateLogConfig(config *configutil.SharedConfig) {
p := &valuesProvider{
flagProvider: func(key string) (string, bool) { return f.getValue(key) },
envVarProvider: func(key string) (string, bool) {
if key == "" {
return "", false
}
return os.LookupEnv(key)
},
}
config.LogLevel = p.getAggregatedConfigValue(flagNameLogLevel, EnvVaultLogLevel, config.LogLevel, "info")
config.LogFormat = p.getAggregatedConfigValue(flagNameLogFormat, EnvVaultLogFormat, config.LogFormat, "")
config.LogFile = p.getAggregatedConfigValue(flagNameLogFile, "", config.LogFile, "")
config.LogRotateDuration = p.getAggregatedConfigValue(flagNameLogRotateDuration, "", config.LogRotateDuration, "")
config.LogRotateBytes = p.getAggregatedConfigValue(flagNameLogRotateBytes, "", config.LogRotateBytes, "")
config.LogRotateMaxFiles = p.getAggregatedConfigValue(flagNameLogRotateMaxFiles, "", config.LogRotateMaxFiles, "")
}