forked from TrueCloudLab/frostfs-s3-gw
[#606] Support log tagging
Signed-off-by: Nikita Zinkevich <n.zinkevich@yadro.com>
This commit is contained in:
parent
ffac62e8b4
commit
e7f620f137
40 changed files with 850 additions and 497 deletions
|
@ -21,20 +21,13 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/version"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
|
||||
"git.frostfs.info/TrueCloudLab/zapjournald"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/ssgreg/journald"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
const (
|
||||
destinationStdout = "stdout"
|
||||
destinationJournald = "journald"
|
||||
|
||||
wildcardPlaceholder = "<wildcard>"
|
||||
)
|
||||
const wildcardPlaceholder = "<wildcard>"
|
||||
|
||||
const (
|
||||
defaultRebalanceInterval = 60 * time.Second
|
||||
|
@ -89,7 +82,8 @@ var (
|
|||
defaultDefaultNamespaces = []string{"", "root"}
|
||||
)
|
||||
|
||||
const ( // Settings.
|
||||
// Settings.
|
||||
const (
|
||||
// Logger.
|
||||
cfgLoggerLevel = "logger.level"
|
||||
cfgLoggerDestination = "logger.destination"
|
||||
|
@ -99,6 +93,11 @@ const ( // Settings.
|
|||
cfgLoggerSamplingThereafter = "logger.sampling.thereafter"
|
||||
cfgLoggerSamplingInterval = "logger.sampling.interval"
|
||||
|
||||
cfgLoggerTags = "logger.tags"
|
||||
cfgLoggerTagsPrefixTmpl = cfgLoggerTags + ".%d."
|
||||
cfgLoggerTagsNameTmpl = cfgLoggerTagsPrefixTmpl + "name"
|
||||
cfgLoggerTagsLevelTmpl = cfgLoggerTagsPrefixTmpl + "level"
|
||||
|
||||
// HttpLogging.
|
||||
cfgHTTPLoggingEnabled = "http_logging.enabled"
|
||||
cfgHTTPLoggingMaxBody = "http_logging.max_body"
|
||||
|
@ -470,14 +469,18 @@ func fetchDefaultPolicy(l *zap.Logger, cfg *viper.Viper) netmap.PlacementPolicy
|
|||
policyStr := cfg.GetString(cfgPolicyDefault)
|
||||
if err := policy.DecodeString(policyStr); err != nil {
|
||||
l.Warn(logs.FailedToParseDefaultLocationConstraint,
|
||||
zap.String("policy", policyStr), zap.String("default", defaultPlacementPolicy), zap.Error(err))
|
||||
zap.String("policy", policyStr), zap.String("default", defaultPlacementPolicy),
|
||||
zap.Error(err), logs.TagField(logs.TagApp))
|
||||
} else {
|
||||
return policy
|
||||
}
|
||||
}
|
||||
|
||||
if err := policy.DecodeString(defaultPlacementPolicy); err != nil {
|
||||
l.Fatal(logs.FailedToParseDefaultDefaultLocationConstraint, zap.String("policy", defaultPlacementPolicy))
|
||||
l.Fatal(logs.FailedToParseDefaultDefaultLocationConstraint,
|
||||
zap.String("policy", defaultPlacementPolicy),
|
||||
logs.TagField(logs.TagApp),
|
||||
)
|
||||
}
|
||||
|
||||
return policy
|
||||
|
@ -490,7 +493,9 @@ func fetchCacheLifetime(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultV
|
|||
l.Error(logs.InvalidLifetimeUsingDefaultValue,
|
||||
zap.String("parameter", cfgEntry),
|
||||
zap.Duration("value in config", lifetime),
|
||||
zap.Duration("default", defaultValue))
|
||||
zap.Duration("default", defaultValue),
|
||||
logs.TagField(logs.TagApp),
|
||||
)
|
||||
} else {
|
||||
return lifetime
|
||||
}
|
||||
|
@ -506,7 +511,9 @@ func fetchCacheSize(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultValue
|
|||
l.Error(logs.InvalidCacheSizeUsingDefaultValue,
|
||||
zap.String("parameter", cfgEntry),
|
||||
zap.Int("value in config", size),
|
||||
zap.Int("default", defaultValue))
|
||||
zap.Int("default", defaultValue),
|
||||
logs.TagField(logs.TagApp),
|
||||
)
|
||||
} else {
|
||||
return size
|
||||
}
|
||||
|
@ -528,7 +535,8 @@ func fetchRemovingCheckInterval(v *viper.Viper, l *zap.Logger) time.Duration {
|
|||
l.Error(logs.InvalidAccessBoxCacheRemovingCheckInterval,
|
||||
zap.String("parameter", cfgAccessBoxCacheRemovingCheckInterval),
|
||||
zap.Duration("value in config", duration),
|
||||
zap.Duration("default", defaultAccessBoxCacheRemovingCheckInterval))
|
||||
zap.Duration("default", defaultAccessBoxCacheRemovingCheckInterval),
|
||||
logs.TagField(logs.TagApp))
|
||||
|
||||
return defaultAccessBoxCacheRemovingCheckInterval
|
||||
}
|
||||
|
@ -542,7 +550,9 @@ func fetchDefaultMaxAge(cfg *viper.Viper, l *zap.Logger) int {
|
|||
if defaultMaxAge <= 0 && defaultMaxAge != -1 {
|
||||
l.Fatal(logs.InvalidDefaultMaxAge,
|
||||
zap.String("parameter", cfgDefaultMaxAge),
|
||||
zap.String("value in config", strconv.Itoa(defaultMaxAge)))
|
||||
zap.String("value in config", strconv.Itoa(defaultMaxAge)),
|
||||
logs.TagField(logs.TagApp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -553,14 +563,19 @@ func fetchRegionMappingPolicies(l *zap.Logger, cfg *viper.Viper) map[string]netm
|
|||
filepath := cfg.GetString(cfgPolicyRegionMapFile)
|
||||
regionPolicyMap, err := readRegionMap(filepath)
|
||||
if err != nil {
|
||||
l.Warn(logs.FailedToReadRegionMapFilePolicies, zap.String("file", filepath), zap.Error(err))
|
||||
l.Warn(logs.FailedToReadRegionMapFilePolicies,
|
||||
zap.String("file", filepath),
|
||||
zap.Error(err),
|
||||
logs.TagField(logs.TagApp))
|
||||
return make(map[string]netmap.PlacementPolicy)
|
||||
}
|
||||
|
||||
regionMap := make(map[string]netmap.PlacementPolicy, len(regionPolicyMap))
|
||||
for region, policy := range regionPolicyMap {
|
||||
if region == api.DefaultLocationConstraint {
|
||||
l.Warn(logs.DefaultLocationConstraintCantBeOverriden, zap.String("policy", policy))
|
||||
l.Warn(logs.DefaultLocationConstraintCantBeOverriden,
|
||||
zap.String("policy", policy),
|
||||
logs.TagField(logs.TagApp))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -575,7 +590,10 @@ func fetchRegionMappingPolicies(l *zap.Logger, cfg *viper.Viper) map[string]netm
|
|||
continue
|
||||
}
|
||||
|
||||
l.Warn(logs.FailedToParseLocationConstraint, zap.String("region", region), zap.String("policy", policy))
|
||||
l.Warn(logs.FailedToParseLocationConstraint,
|
||||
zap.String("region", region),
|
||||
zap.String("policy", policy),
|
||||
logs.TagField(logs.TagApp))
|
||||
}
|
||||
|
||||
return regionMap
|
||||
|
@ -607,7 +625,11 @@ func fetchDefaultCopiesNumbers(l *zap.Logger, v *viper.Viper) []uint32 {
|
|||
parsedValue, err := strconv.ParseUint(unparsed[i], 10, 32)
|
||||
if err != nil {
|
||||
l.Warn(logs.FailedToParseDefaultCopiesNumbers,
|
||||
zap.Strings("copies numbers", unparsed), zap.Uint32s("default", defaultCopiesNumbers), zap.Error(err))
|
||||
zap.Strings("copies numbers", unparsed),
|
||||
zap.Uint32s("default", defaultCopiesNumbers),
|
||||
zap.Error(err),
|
||||
logs.TagField(logs.TagApp),
|
||||
)
|
||||
return defaultCopiesNumbers
|
||||
}
|
||||
result[i] = uint32(parsedValue)
|
||||
|
@ -663,15 +685,17 @@ func fetchCopiesNumbers(l *zap.Logger, v *viper.Viper) map[string][]uint32 {
|
|||
for j := range vector {
|
||||
parsedValue, err := strconv.ParseUint(vector[j], 10, 32)
|
||||
if err != nil {
|
||||
l.Warn(logs.FailedToParseCopiesNumbers, zap.String("location", constraint),
|
||||
zap.Strings("copies numbers", vector), zap.Error(err))
|
||||
l.Warn(logs.FailedToParseCopiesNumbers,
|
||||
zap.String("location", constraint),
|
||||
zap.Strings("copies numbers", vector), zap.Error(err),
|
||||
logs.TagField(logs.TagApp))
|
||||
continue
|
||||
}
|
||||
vector32[j] = uint32(parsedValue)
|
||||
}
|
||||
|
||||
copiesNums[constraint] = vector32
|
||||
l.Info(logs.ConstraintAdded, zap.String("location", constraint), zap.Strings("copies numbers", vector))
|
||||
l.Info(logs.ConstraintAdded, zap.String("location", constraint), zap.Strings("copies numbers", vector), logs.TagField(logs.TagApp))
|
||||
}
|
||||
return copiesNums
|
||||
}
|
||||
|
@ -680,7 +704,9 @@ func fetchDefaultNamespaces(l *zap.Logger, v *viper.Viper) []string {
|
|||
defaultNamespaces := v.GetStringSlice(cfgKludgeDefaultNamespaces)
|
||||
if len(defaultNamespaces) == 0 {
|
||||
defaultNamespaces = defaultDefaultNamespaces
|
||||
l.Warn(logs.DefaultNamespacesCannotBeEmpty, zap.Strings("namespaces", defaultNamespaces))
|
||||
l.Warn(logs.DefaultNamespacesCannotBeEmpty,
|
||||
zap.Strings("namespaces", defaultNamespaces),
|
||||
logs.TagField(logs.TagApp))
|
||||
}
|
||||
|
||||
for i := range defaultNamespaces { // to be set namespaces in env variable as `S3_GW_KLUDGE_DEFAULT_NAMESPACES="" 'root'`
|
||||
|
@ -704,7 +730,7 @@ func fetchNamespacesConfig(l *zap.Logger, v *viper.Viper) (NamespacesConfig, []s
|
|||
|
||||
nsConfig, err := readNamespacesConfig(v.GetString(cfgNamespacesConfig))
|
||||
if err != nil {
|
||||
l.Warn(logs.FailedToParseNamespacesConfig, zap.Error(err))
|
||||
l.Warn(logs.FailedToParseNamespacesConfig, zap.Error(err), logs.TagField(logs.TagApp))
|
||||
}
|
||||
|
||||
defaultNamespacesNames := fetchDefaultNamespaces(l, v)
|
||||
|
@ -718,11 +744,13 @@ func fetchNamespacesConfig(l *zap.Logger, v *viper.Viper) (NamespacesConfig, []s
|
|||
}
|
||||
|
||||
if len(overrideDefaults) > 0 {
|
||||
l.Warn(logs.DefaultNamespaceConfigValuesBeOverwritten)
|
||||
l.Warn(logs.DefaultNamespaceConfigValuesBeOverwritten, logs.TagField(logs.TagApp))
|
||||
defaultNSValue.LocationConstraints = overrideDefaults[0].LocationConstraints
|
||||
defaultNSValue.CopiesNumbers = overrideDefaults[0].CopiesNumbers
|
||||
if len(overrideDefaults) > 1 {
|
||||
l.Warn(logs.MultipleDefaultOverridesFound, zap.String("name", overrideDefaults[0].Name))
|
||||
l.Warn(logs.MultipleDefaultOverridesFound,
|
||||
zap.String("name", overrideDefaults[0].Name),
|
||||
logs.TagField(logs.TagApp))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -765,7 +793,7 @@ func fetchPeers(l *zap.Logger, v *viper.Viper) []pool.NodeParam {
|
|||
priority := v.GetInt(key + "priority")
|
||||
|
||||
if address == "" {
|
||||
l.Warn(logs.SkipEmptyAddress)
|
||||
l.Warn(logs.SkipEmptyAddress, logs.TagField(logs.TagApp))
|
||||
break
|
||||
}
|
||||
if weight <= 0 { // unspecified or wrong
|
||||
|
@ -780,7 +808,9 @@ func fetchPeers(l *zap.Logger, v *viper.Viper) []pool.NodeParam {
|
|||
l.Info(logs.AddedStoragePeer,
|
||||
zap.Int("priority", priority),
|
||||
zap.String("address", address),
|
||||
zap.Float64("weight", weight))
|
||||
zap.Float64("weight", weight),
|
||||
logs.TagField(logs.TagApp),
|
||||
)
|
||||
}
|
||||
|
||||
return nodes
|
||||
|
@ -804,7 +834,7 @@ func fetchServers(v *viper.Viper, log *zap.Logger) []ServerInfo {
|
|||
}
|
||||
|
||||
if _, ok := seen[serverInfo.Address]; ok {
|
||||
log.Warn(logs.WarnDuplicateAddress, zap.String("address", serverInfo.Address))
|
||||
log.Warn(logs.WarnDuplicateAddress, zap.String("address", serverInfo.Address), logs.TagField(logs.TagApp))
|
||||
continue
|
||||
}
|
||||
seen[serverInfo.Address] = struct{}{}
|
||||
|
@ -833,13 +863,13 @@ func fetchVHSNamespaces(v *viper.Viper, log *zap.Logger) map[string]bool {
|
|||
nsMap := v.GetStringMap(cfgVHSNamespaces)
|
||||
for ns, val := range nsMap {
|
||||
if _, ok := vhsNamespacesEnabled[ns]; ok {
|
||||
log.Warn(logs.WarnDuplicateNamespaceVHS, zap.String("namespace", ns))
|
||||
log.Warn(logs.WarnDuplicateNamespaceVHS, zap.String("namespace", ns), logs.TagField(logs.TagApp))
|
||||
continue
|
||||
}
|
||||
|
||||
enabledFlag, ok := val.(bool)
|
||||
if !ok {
|
||||
log.Warn(logs.WarnValueVHSEnabledFlagWrongType, zap.String("namespace", ns))
|
||||
log.Warn(logs.WarnValueVHSEnabledFlagWrongType, zap.String("namespace", ns), logs.TagField(logs.TagApp))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -923,6 +953,41 @@ func fetchTombstoneWorkerPoolSize(v *viper.Viper) int {
|
|||
return tombstoneWorkerPoolSize
|
||||
}
|
||||
|
||||
func fetchLogTagsConfig(v *viper.Viper) (map[string]zapcore.Level, error) {
|
||||
res := make(map[string]zapcore.Level)
|
||||
|
||||
defaultLevel := v.GetString(cfgLoggerLevel)
|
||||
var defaultLvl zapcore.Level
|
||||
if err := defaultLvl.Set(defaultLevel); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse log level, unknown level: '%s'", defaultLevel)
|
||||
}
|
||||
|
||||
for i := 0; ; i++ {
|
||||
name := v.GetString(fmt.Sprintf(cfgLoggerTagsNameTmpl, i))
|
||||
if name == "" {
|
||||
break
|
||||
}
|
||||
|
||||
lvl := defaultLvl
|
||||
level := v.GetString(fmt.Sprintf(cfgLoggerTagsLevelTmpl, i))
|
||||
if level != "" {
|
||||
if err := lvl.Set(level); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse log tags config, unknown level: '%s'", level)
|
||||
}
|
||||
}
|
||||
|
||||
res[name] = lvl
|
||||
}
|
||||
|
||||
if len(res) == 0 && !v.IsSet(cfgLoggerTags) {
|
||||
for _, tag := range defaultTags {
|
||||
res[tag] = defaultLvl
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func newViper(flags *pflag.FlagSet) (*viper.Viper, error) {
|
||||
v := viper.New()
|
||||
|
||||
|
@ -1233,129 +1298,19 @@ type LoggerAppSettings interface {
|
|||
DroppedLogsInc()
|
||||
}
|
||||
|
||||
func pickLogger(v *viper.Viper, settings LoggerAppSettings) *Logger {
|
||||
lvl, err := getLogLevel(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
dest := v.GetString(cfgLoggerDestination)
|
||||
|
||||
switch dest {
|
||||
case destinationStdout:
|
||||
return newStdoutLogger(v, lvl, settings)
|
||||
case destinationJournald:
|
||||
return newJournaldLogger(v, lvl, settings)
|
||||
default:
|
||||
panic(fmt.Sprintf("wrong destination for logger: %s", dest))
|
||||
}
|
||||
}
|
||||
|
||||
// newStdoutLogger constructs a Logger instance for the current application.
|
||||
// Panics on failure.
|
||||
//
|
||||
// Logger contains a logger is built from zap's production logging configuration with:
|
||||
// - parameterized level (debug by default)
|
||||
// - console encoding
|
||||
// - ISO8601 time encoding
|
||||
// - sampling intervals
|
||||
//
|
||||
// and atomic log level to dynamically change it.
|
||||
//
|
||||
// Logger records a stack trace for all messages at or above fatal level.
|
||||
//
|
||||
// See also zapcore.Level, zap.NewProductionConfig, zap.AddStacktrace.
|
||||
func newStdoutLogger(v *viper.Viper, lvl zapcore.Level, settings LoggerAppSettings) *Logger {
|
||||
stdout := zapcore.AddSync(os.Stderr)
|
||||
level := zap.NewAtomicLevelAt(lvl)
|
||||
|
||||
consoleOutCore := zapcore.NewCore(newLogEncoder(), stdout, level)
|
||||
|
||||
consoleOutCore = applyZapCoreMiddlewares(consoleOutCore, v, settings)
|
||||
|
||||
return &Logger{
|
||||
logger: zap.New(consoleOutCore, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel))),
|
||||
lvl: level,
|
||||
}
|
||||
}
|
||||
|
||||
func newJournaldLogger(v *viper.Viper, lvl zapcore.Level, settings LoggerAppSettings) *Logger {
|
||||
level := zap.NewAtomicLevelAt(lvl)
|
||||
|
||||
encoder := zapjournald.NewPartialEncoder(newLogEncoder(), zapjournald.SyslogFields)
|
||||
|
||||
core := zapjournald.NewCore(level, encoder, &journald.Journal{}, zapjournald.SyslogFields)
|
||||
coreWithContext := core.With([]zapcore.Field{
|
||||
zapjournald.SyslogFacility(zapjournald.LogDaemon),
|
||||
zapjournald.SyslogIdentifier(),
|
||||
zapjournald.SyslogPid(),
|
||||
})
|
||||
|
||||
coreWithContext = applyZapCoreMiddlewares(coreWithContext, v, settings)
|
||||
|
||||
l := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
|
||||
|
||||
return &Logger{
|
||||
logger: l,
|
||||
lvl: level,
|
||||
}
|
||||
}
|
||||
|
||||
func newLogEncoder() zapcore.Encoder {
|
||||
c := zap.NewProductionEncoderConfig()
|
||||
c.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
|
||||
return zapcore.NewConsoleEncoder(c)
|
||||
}
|
||||
|
||||
func applyZapCoreMiddlewares(core zapcore.Core, v *viper.Viper, settings LoggerAppSettings) zapcore.Core {
|
||||
if v.GetBool(cfgLoggerSamplingEnabled) {
|
||||
core = zapcore.NewSamplerWithOptions(core,
|
||||
v.GetDuration(cfgLoggerSamplingInterval),
|
||||
v.GetInt(cfgLoggerSamplingInitial),
|
||||
v.GetInt(cfgLoggerSamplingThereafter),
|
||||
zapcore.SamplerHook(func(_ zapcore.Entry, dec zapcore.SamplingDecision) {
|
||||
if dec&zapcore.LogDropped > 0 {
|
||||
settings.DroppedLogsInc()
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
return core
|
||||
}
|
||||
|
||||
func getLogLevel(v *viper.Viper) (zapcore.Level, error) {
|
||||
var lvl zapcore.Level
|
||||
lvlStr := v.GetString(cfgLoggerLevel)
|
||||
err := lvl.UnmarshalText([]byte(lvlStr))
|
||||
if err != nil {
|
||||
return lvl, fmt.Errorf("incorrect logger level configuration %s (%v), "+
|
||||
"value should be one of %v", lvlStr, err, [...]zapcore.Level{
|
||||
zapcore.DebugLevel,
|
||||
zapcore.InfoLevel,
|
||||
zapcore.WarnLevel,
|
||||
zapcore.ErrorLevel,
|
||||
zapcore.DPanicLevel,
|
||||
zapcore.PanicLevel,
|
||||
zapcore.FatalLevel,
|
||||
})
|
||||
}
|
||||
return lvl, nil
|
||||
}
|
||||
|
||||
func validateDomains(domains []string, log *zap.Logger) []string {
|
||||
validDomains := make([]string, 0, len(domains))
|
||||
LOOP:
|
||||
for _, domain := range domains {
|
||||
if strings.Contains(domain, ":") {
|
||||
log.Warn(logs.WarnDomainContainsPort, zap.String("domain", domain))
|
||||
log.Warn(logs.WarnDomainContainsPort, zap.String("domain", domain), logs.TagField(logs.TagApp))
|
||||
continue
|
||||
}
|
||||
|
||||
domainParts := strings.Split(domain, ".")
|
||||
for _, part := range domainParts {
|
||||
if strings.ContainsAny(part, "<>") && part != wildcardPlaceholder {
|
||||
log.Warn(logs.WarnDomainContainsInvalidPlaceholder, zap.String("domain", domain))
|
||||
log.Warn(logs.WarnDomainContainsInvalidPlaceholder, zap.String("domain", domain), logs.TagField(logs.TagApp))
|
||||
continue LOOP
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue