[#147] Add sampling configuration

Signed-off-by: Pavel Pogodaev <p.pogodaev@yadro.com>
This commit is contained in:
Pavel Pogodaev 2024-09-26 17:48:19 +03:00 committed by pogpp
parent c8473498ae
commit 77eb474581
4 changed files with 75 additions and 26 deletions

View file

@ -41,6 +41,8 @@ const (
defaultConnectTimeout = 10 * time.Second defaultConnectTimeout = 10 * time.Second
defaultStreamTimeout = 10 * time.Second defaultStreamTimeout = 10 * time.Second
defaultLoggerSamplerInterval = 1 * time.Second
defaultShutdownTimeout = 15 * time.Second defaultShutdownTimeout = 15 * time.Second
defaultPoolErrorThreshold uint32 = 100 defaultPoolErrorThreshold uint32 = 100
@ -91,6 +93,11 @@ const (
cfgLoggerLevel = "logger.level" cfgLoggerLevel = "logger.level"
cfgLoggerDestination = "logger.destination" cfgLoggerDestination = "logger.destination"
cfgLoggerSamplingEnabled = "logger.sampling.enabled"
cfgLoggerSamplingInitial = "logger.sampling.initial"
cfgLoggerSamplingThereafter = "logger.sampling.thereafter"
cfgLoggerSamplingInterval = "logger.sampling.interval"
// Wallet. // Wallet.
cfgWalletPassphrase = "wallet.passphrase" cfgWalletPassphrase = "wallet.passphrase"
cfgWalletPath = "wallet.path" cfgWalletPath = "wallet.path"
@ -188,6 +195,10 @@ func settings() *viper.Viper {
// logger: // logger:
v.SetDefault(cfgLoggerLevel, "debug") v.SetDefault(cfgLoggerLevel, "debug")
v.SetDefault(cfgLoggerDestination, "stdout") v.SetDefault(cfgLoggerDestination, "stdout")
v.SetDefault(cfgLoggerSamplingEnabled, false)
v.SetDefault(cfgLoggerSamplingThereafter, 100)
v.SetDefault(cfgLoggerSamplingInitial, 100)
v.SetDefault(cfgLoggerSamplingInterval, defaultLoggerSamplerInterval)
// pool: // pool:
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold) v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)
@ -386,9 +397,9 @@ func pickLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
switch dest { switch dest {
case destinationStdout: case destinationStdout:
return newStdoutLogger(lvl) return newStdoutLogger(v, lvl)
case destinationJournald: case destinationJournald:
return newJournaldLogger(lvl) return newJournaldLogger(v, lvl)
default: default:
panic(fmt.Sprintf("wrong destination for logger: %s", dest)) panic(fmt.Sprintf("wrong destination for logger: %s", dest))
} }
@ -405,39 +416,59 @@ func pickLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
// Logger records a stack trace for all messages at or above fatal level. // Logger records a stack trace for all messages at or above fatal level.
// //
// See also zapcore.Level, zap.NewProductionConfig, zap.AddStacktrace. // See also zapcore.Level, zap.NewProductionConfig, zap.AddStacktrace.
func newStdoutLogger(lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) { func newStdoutLogger(v *viper.Viper, lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
c := zap.NewProductionConfig() stdout := zapcore.AddSync(os.Stderr)
c.Level = zap.NewAtomicLevelAt(lvl) level := zap.NewAtomicLevelAt(lvl)
c.Encoding = "console"
c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
l, err := c.Build( consoleOutCore := zapcore.NewCore(newLogEncoder(), stdout, level)
zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)), consoleOutCore = samplingEnabling(v, consoleOutCore)
)
if err != nil {
panic(fmt.Sprintf("build zap logger instance: %v", err))
}
return l, c.Level l := zap.New(consoleOutCore, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
return l, level
} }
func newJournaldLogger(lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) { func newJournaldLogger(v *viper.Viper, lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
c := zap.NewProductionConfig() level := zap.NewAtomicLevelAt(lvl)
c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
c.Level = zap.NewAtomicLevelAt(lvl)
encoder := zapjournald.NewPartialEncoder(zapcore.NewConsoleEncoder(c.EncoderConfig), zapjournald.SyslogFields) encoder := zapjournald.NewPartialEncoder(newLogEncoder(), zapjournald.SyslogFields)
core := zapjournald.NewCore(c.Level, encoder, &journald.Journal{}, zapjournald.SyslogFields) core := zapjournald.NewCore(level, encoder, &journald.Journal{}, zapjournald.SyslogFields)
coreWithContext := core.With([]zapcore.Field{ coreWithContext := core.With([]zapcore.Field{
zapjournald.SyslogFacility(zapjournald.LogDaemon), zapjournald.SyslogFacility(zapjournald.LogDaemon),
zapjournald.SyslogIdentifier(), zapjournald.SyslogIdentifier(),
zapjournald.SyslogPid(), zapjournald.SyslogPid(),
}) })
coreWithContext = samplingEnabling(v, coreWithContext)
l := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel))) l := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
return l, c.Level return l, level
}
func newLogEncoder() zapcore.Encoder {
c := zap.NewProductionEncoderConfig()
c.EncodeTime = zapcore.ISO8601TimeEncoder
return zapcore.NewConsoleEncoder(c)
}
func samplingEnabling(v *viper.Viper, core zapcore.Core) zapcore.Core {
// Zap samples by logging the first cgfLoggerSamplingInitial entries with a given level
// and message within the specified time interval.
// In the above config, only the first cgfLoggerSamplingInitial log entries with the same level and message
// are recorded in cfgLoggerSamplingInterval interval. Every other log entry will be dropped within the interval since
// cfgLoggerSamplingThereafter is specified here.
if v.GetBool(cfgLoggerSamplingEnabled) {
core = zapcore.NewSamplerWithOptions(
core,
v.GetDuration(cfgLoggerSamplingInterval),
v.GetInt(cfgLoggerSamplingInitial),
v.GetInt(cfgLoggerSamplingThereafter),
)
}
return core
} }
func getLogLevel(v *viper.Viper) (zapcore.Level, error) { func getLogLevel(v *viper.Viper) (zapcore.Level, error) {

View file

@ -14,8 +14,12 @@ HTTP_GW_PPROF_ADDRESS=localhost:8083
HTTP_GW_PROMETHEUS_ENABLED=true HTTP_GW_PROMETHEUS_ENABLED=true
HTTP_GW_PROMETHEUS_ADDRESS=localhost:8084 HTTP_GW_PROMETHEUS_ADDRESS=localhost:8084
# Log level. # Logger.
HTTP_GW_LOGGER_LEVEL=debug HTTP_GW_LOGGER_LEVEL=debug
HTTP_GW_LOGGER_SAMPLING_ENABLED=false
HTTP_GW_LOGGER_SAMPLING_INITIAL=100
HTTP_GW_LOGGER_SAMPLING_THEREAFTER=100
HTTP_GW_LOGGER_SAMPLING_INTERVAL=1s
HTTP_GW_SERVER_0_ADDRESS=0.0.0.0:443 HTTP_GW_SERVER_0_ADDRESS=0.0.0.0:443
HTTP_GW_SERVER_0_TLS_ENABLED=false HTTP_GW_SERVER_0_TLS_ENABLED=false

View file

@ -18,6 +18,11 @@ tracing:
logger: logger:
level: debug # Log level. level: debug # Log level.
destination: stdout destination: stdout
sampling:
enabled: false
initial: 100
thereafter: 100
interval: 1s
server: server:
- address: 0.0.0.0:8080 - address: 0.0.0.0:8080

View file

@ -164,12 +164,21 @@ server:
logger: logger:
level: debug level: debug
destination: stdout destination: stdout
sampling:
enabled: false
initial: 100
thereafter: 100
interval: 1s
``` ```
| Parameter | Type | SIGHUP reload | Default value | Description | | Parameter | Type | SIGHUP reload | Default value | Description |
|---------------|----------|---------------|---------------|----------------------------------------------------------------------------------------------------| |-----------------------|------------|---------------|---------------|----------------------------------------------------------------------------------------------------|
| `level` | `string` | yes | `debug` | Logging level.<br/>Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. | | `level` | `string` | yes | `debug` | Logging level.<br/>Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. |
| `destination` | `string` | no | `stdout` | Destination for logger: `stdout` or `journald` | | `destination` | `string` | no | `stdout` | Destination for logger: `stdout` or `journald` |
| `sampling.enabled` | `bool` | no | false | Sampling enabling flag. |
| `sampling.initial` | `int` | no | '100' | Sampling count of first log entries. |
| `sampling.thereafter` | `int` | no | '100' | Sampling count of entries after an `interval`. |
| `sampling.interval` | `duration` | no | '1s' | Sampling interval of messaging similar entries. |
# `web` section # `web` section