From 77eb4745814640ff34a6b787712a427c1ff93519 Mon Sep 17 00:00:00 2001
From: Pavel Pogodaev
Date: Thu, 26 Sep 2024 17:48:19 +0300
Subject: [PATCH] [#147] Add sampling configuration
Signed-off-by: Pavel Pogodaev
---
cmd/http-gw/settings.go | 73 +++++++++++++++++++++++++++-----------
config/config.env | 6 +++-
config/config.yaml | 5 +++
docs/gate-configuration.md | 17 ++++++---
4 files changed, 75 insertions(+), 26 deletions(-)
diff --git a/cmd/http-gw/settings.go b/cmd/http-gw/settings.go
index 3fe0023..d9bbc53 100644
--- a/cmd/http-gw/settings.go
+++ b/cmd/http-gw/settings.go
@@ -41,6 +41,8 @@ const (
defaultConnectTimeout = 10 * time.Second
defaultStreamTimeout = 10 * time.Second
+ defaultLoggerSamplerInterval = 1 * time.Second
+
defaultShutdownTimeout = 15 * time.Second
defaultPoolErrorThreshold uint32 = 100
@@ -91,6 +93,11 @@ const (
cfgLoggerLevel = "logger.level"
cfgLoggerDestination = "logger.destination"
+ cfgLoggerSamplingEnabled = "logger.sampling.enabled"
+ cfgLoggerSamplingInitial = "logger.sampling.initial"
+ cfgLoggerSamplingThereafter = "logger.sampling.thereafter"
+ cfgLoggerSamplingInterval = "logger.sampling.interval"
+
// Wallet.
cfgWalletPassphrase = "wallet.passphrase"
cfgWalletPath = "wallet.path"
@@ -188,6 +195,10 @@ func settings() *viper.Viper {
// logger:
v.SetDefault(cfgLoggerLevel, "debug")
v.SetDefault(cfgLoggerDestination, "stdout")
+ v.SetDefault(cfgLoggerSamplingEnabled, false)
+ v.SetDefault(cfgLoggerSamplingThereafter, 100)
+ v.SetDefault(cfgLoggerSamplingInitial, 100)
+ v.SetDefault(cfgLoggerSamplingInterval, defaultLoggerSamplerInterval)
// pool:
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)
@@ -386,9 +397,9 @@ func pickLogger(v *viper.Viper) (*zap.Logger, zap.AtomicLevel) {
switch dest {
case destinationStdout:
- return newStdoutLogger(lvl)
+ return newStdoutLogger(v, lvl)
case destinationJournald:
- return newJournaldLogger(lvl)
+ return newJournaldLogger(v, lvl)
default:
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.
//
// See also zapcore.Level, zap.NewProductionConfig, zap.AddStacktrace.
-func newStdoutLogger(lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
- c := zap.NewProductionConfig()
- c.Level = zap.NewAtomicLevelAt(lvl)
- c.Encoding = "console"
- c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
+func newStdoutLogger(v *viper.Viper, lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
+ stdout := zapcore.AddSync(os.Stderr)
+ level := zap.NewAtomicLevelAt(lvl)
- l, err := c.Build(
- zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)),
- )
- if err != nil {
- panic(fmt.Sprintf("build zap logger instance: %v", err))
- }
+ consoleOutCore := zapcore.NewCore(newLogEncoder(), stdout, level)
+ consoleOutCore = samplingEnabling(v, consoleOutCore)
- 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) {
- c := zap.NewProductionConfig()
- c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
- c.Level = zap.NewAtomicLevelAt(lvl)
+func newJournaldLogger(v *viper.Viper, lvl zapcore.Level) (*zap.Logger, zap.AtomicLevel) {
+ 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{
zapjournald.SyslogFacility(zapjournald.LogDaemon),
zapjournald.SyslogIdentifier(),
zapjournald.SyslogPid(),
})
+ coreWithContext = samplingEnabling(v, coreWithContext)
+
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) {
diff --git a/config/config.env b/config/config.env
index b7347d7..e1d5e7d 100644
--- a/config/config.env
+++ b/config/config.env
@@ -14,8 +14,12 @@ HTTP_GW_PPROF_ADDRESS=localhost:8083
HTTP_GW_PROMETHEUS_ENABLED=true
HTTP_GW_PROMETHEUS_ADDRESS=localhost:8084
-# Log level.
+# Logger.
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_TLS_ENABLED=false
diff --git a/config/config.yaml b/config/config.yaml
index ef57612..1b87fe9 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -18,6 +18,11 @@ tracing:
logger:
level: debug # Log level.
destination: stdout
+ sampling:
+ enabled: false
+ initial: 100
+ thereafter: 100
+ interval: 1s
server:
- address: 0.0.0.0:8080
diff --git a/docs/gate-configuration.md b/docs/gate-configuration.md
index e0bb336..fb5ad2f 100644
--- a/docs/gate-configuration.md
+++ b/docs/gate-configuration.md
@@ -164,12 +164,21 @@ server:
logger:
level: debug
destination: stdout
+ sampling:
+ enabled: false
+ initial: 100
+ thereafter: 100
+ interval: 1s
```
-| Parameter | Type | SIGHUP reload | Default value | Description |
-|---------------|----------|---------------|---------------|----------------------------------------------------------------------------------------------------|
-| `level` | `string` | yes | `debug` | Logging level.
Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. |
-| `destination` | `string` | no | `stdout` | Destination for logger: `stdout` or `journald` |
+| Parameter | Type | SIGHUP reload | Default value | Description |
+|-----------------------|------------|---------------|---------------|----------------------------------------------------------------------------------------------------|
+| `level` | `string` | yes | `debug` | Logging level.
Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal`. |
+| `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