config: add log encoding option

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
Anna Shaleva 2025-03-21 18:00:04 +03:00
parent 3cfba1bde4
commit 83e138000b
6 changed files with 107 additions and 51 deletions

View file

@ -295,8 +295,9 @@ var (
// If the program is run in TTY then logger adds timestamp to its entries. // If the program is run in TTY then logger adds timestamp to its entries.
func HandleLoggingParams(ctx *cli.Context, cfg config.ApplicationConfiguration) (*zap.Logger, *zap.AtomicLevel, func() error, error) { func HandleLoggingParams(ctx *cli.Context, cfg config.ApplicationConfiguration) (*zap.Logger, *zap.AtomicLevel, func() error, error) {
var ( var (
level = zapcore.InfoLevel level = zapcore.InfoLevel
err error encoding = "console"
err error
) )
if len(cfg.LogLevel) > 0 { if len(cfg.LogLevel) > 0 {
level, err = zapcore.ParseLevel(cfg.LogLevel) level, err = zapcore.ParseLevel(cfg.LogLevel)
@ -304,6 +305,9 @@ func HandleLoggingParams(ctx *cli.Context, cfg config.ApplicationConfiguration)
return nil, nil, nil, fmt.Errorf("log setting: %w", err) return nil, nil, nil, fmt.Errorf("log setting: %w", err)
} }
} }
if len(cfg.LogEncoding) > 0 {
encoding = cfg.LogEncoding
}
if ctx != nil && ctx.Bool("debug") { if ctx != nil && ctx.Bool("debug") {
level = zapcore.DebugLevel level = zapcore.DebugLevel
} }
@ -318,7 +322,7 @@ func HandleLoggingParams(ctx *cli.Context, cfg config.ApplicationConfiguration)
} else { } else {
cc.EncoderConfig.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {} cc.EncoderConfig.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {}
} }
cc.Encoding = "console" cc.Encoding = encoding
cc.Level = zap.NewAtomicLevelAt(level) cc.Level = zap.NewAtomicLevelAt(level)
cc.Sampling = nil cc.Sampling = nil

View file

@ -96,7 +96,9 @@ func TestHandleLoggingParams(t *testing.T) {
logfile := filepath.Join(d, "logdir") logfile := filepath.Join(d, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg := config.ApplicationConfiguration{ cfg := config.ApplicationConfiguration{
LogPath: filepath.Join(logfile, "file.log"), Logger: config.Logger{
LogPath: filepath.Join(logfile, "file.log"),
},
} }
_, lvl, closer, err := options.HandleLoggingParams(ctx, cfg) _, lvl, closer, err := options.HandleLoggingParams(ctx, cfg)
require.Error(t, err) require.Error(t, err)
@ -106,8 +108,10 @@ func TestHandleLoggingParams(t *testing.T) {
t.Run("broken level", func(t *testing.T) { t.Run("broken level", func(t *testing.T) {
cfg := config.ApplicationConfiguration{ cfg := config.ApplicationConfiguration{
LogPath: testLog, Logger: config.Logger{
LogLevel: "qwerty", LogPath: testLog,
LogLevel: "qwerty",
},
} }
_, lvl, closer, err := options.HandleLoggingParams(ctx, cfg) _, lvl, closer, err := options.HandleLoggingParams(ctx, cfg)
require.Error(t, err) require.Error(t, err)
@ -117,7 +121,9 @@ func TestHandleLoggingParams(t *testing.T) {
t.Run("default", func(t *testing.T) { t.Run("default", func(t *testing.T) {
cfg := config.ApplicationConfiguration{ cfg := config.ApplicationConfiguration{
LogPath: testLog, Logger: config.Logger{
LogPath: testLog,
},
} }
logger, lvl, closer, err := options.HandleLoggingParams(ctx, cfg) logger, lvl, closer, err := options.HandleLoggingParams(ctx, cfg)
require.NotNil(t, lvl) require.NotNil(t, lvl)
@ -134,8 +140,10 @@ func TestHandleLoggingParams(t *testing.T) {
t.Run("warn", func(t *testing.T) { t.Run("warn", func(t *testing.T) {
cfg := config.ApplicationConfiguration{ cfg := config.ApplicationConfiguration{
LogPath: testLog, Logger: config.Logger{
LogLevel: "warn", LogPath: testLog,
LogLevel: "warn",
},
} }
logger, lvl, closer, err := options.HandleLoggingParams(ctx, cfg) logger, lvl, closer, err := options.HandleLoggingParams(ctx, cfg)
require.NoError(t, err) require.NoError(t, err)
@ -151,7 +159,9 @@ func TestHandleLoggingParams(t *testing.T) {
t.Run("debug", func(t *testing.T) { t.Run("debug", func(t *testing.T) {
cfg := config.ApplicationConfiguration{ cfg := config.ApplicationConfiguration{
LogPath: testLog, Logger: config.Logger{
LogPath: testLog,
},
} }
*debug = true *debug = true
t.Cleanup(func() { *debug = false }) t.Cleanup(func() { *debug = false })

View file

@ -17,6 +17,7 @@ node-related settings described in the table below.
| Section | Type | Default value | Description | | Section | Type | Default value | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| DBConfiguration | [DB Configuration](#DB-Configuration) | | Describes configuration for database. See the [DB Configuration](#DB-Configuration) section for details. | | DBConfiguration | [DB Configuration](#DB-Configuration) | | Describes configuration for database. See the [DB Configuration](#DB-Configuration) section for details. |
| LogEncoding | `string` | "console" | Logs output format (can be "console" or "json"). |
| LogLevel | `string` | "info" | Minimal logged messages level (can be "debug", "info", "warn", "error", "dpanic", "panic" or "fatal"). | | LogLevel | `string` | "info" | Minimal logged messages level (can be "debug", "info", "warn", "error", "dpanic", "panic" or "fatal"). |
| GarbageCollectionPeriod | `uint32` | 10000 | Controls MPT garbage collection interval (in blocks) for configurations with `RemoveUntraceableBlocks` enabled and `KeepOnlyLatestState` disabled. In this mode the node stores a number of MPT trees (corresponding to `MaxTraceableBlocks` and `StateSyncInterval`), but the DB needs to be clean from old entries from time to time. Doing it too often will cause too much processing overhead (it requires going through the whole DB which can take minutes), doing it too rarely will leave more useless data in the DB. Always compare this to `MaxTraceableBlocks`, values lower than 10% of it are likely too low, values higher than 50% are likely to leave more garbage than is possible to collect. The default value is more aligned with NeoFS networks that have low MTB values, but for N3 mainnet it's too low. | | GarbageCollectionPeriod | `uint32` | 10000 | Controls MPT garbage collection interval (in blocks) for configurations with `RemoveUntraceableBlocks` enabled and `KeepOnlyLatestState` disabled. In this mode the node stores a number of MPT trees (corresponding to `MaxTraceableBlocks` and `StateSyncInterval`), but the DB needs to be clean from old entries from time to time. Doing it too often will cause too much processing overhead (it requires going through the whole DB which can take minutes), doing it too rarely will leave more useless data in the DB. Always compare this to `MaxTraceableBlocks`, values lower than 10% of it are likely too low, values higher than 50% are likely to leave more garbage than is possible to collect. The default value is more aligned with NeoFS networks that have low MTB values, but for N3 mainnet it's too low. |
| KeepOnlyLatestState | `bool` | `false` | Specifies if MPT should only store the latest state (or a set of latest states, see `P2PStateExchangeExtensions` section in the ProtocolConfiguration for details). If true, DB size will be smaller, but older roots won't be accessible. This value should remain the same for the same database. | | | KeepOnlyLatestState | `bool` | `false` | Specifies if MPT should only store the latest state (or a set of latest states, see `P2PStateExchangeExtensions` section in the ProtocolConfiguration for details). If true, DB size will be smaller, but older roots won't be accessible. This value should remain the same for the same database. | |

View file

@ -15,8 +15,7 @@ type ApplicationConfiguration struct {
DBConfiguration dbconfig.DBConfiguration `yaml:"DBConfiguration"` DBConfiguration dbconfig.DBConfiguration `yaml:"DBConfiguration"`
LogLevel string `yaml:"LogLevel"` Logger `yaml:",inline"`
LogPath string `yaml:"LogPath"`
P2P P2P `yaml:"P2P"` P2P P2P `yaml:"P2P"`
@ -153,5 +152,8 @@ func (a *ApplicationConfiguration) Validate() error {
if err := a.RPC.Validate(); err != nil { if err := a.RPC.Validate(); err != nil {
return fmt.Errorf("invalid RPC config: %w", err) return fmt.Errorf("invalid RPC config: %w", err)
} }
if err := a.Logger.Validate(); err != nil {
return fmt.Errorf("invalid logger config: %w", err)
}
return nil return nil
} }

View file

@ -135,9 +135,9 @@ func TestGetAddresses(t *testing.T) {
} }
} }
func TestNeoFSBlockFetcherValidation(t *testing.T) { func TestApplicationConfiguration_Validate(t *testing.T) {
type testcase struct { type testcase struct {
cfg NeoFSBlockFetcher cfg ApplicationConfiguration
shouldFail bool shouldFail bool
errMsg string errMsg string
} }
@ -146,66 +146,85 @@ func TestNeoFSBlockFetcherValidation(t *testing.T) {
cases := []testcase{ cases := []testcase{
{ {
cfg: NeoFSBlockFetcher{ cfg: ApplicationConfiguration{
InternalService: InternalService{Enabled: true}, NeoFSBlockFetcher: NeoFSBlockFetcher{
Timeout: time.Second, InternalService: InternalService{Enabled: true},
ContainerID: validContainerID, Timeout: time.Second,
Addresses: []string{"127.0.0.1"}, ContainerID: validContainerID,
OIDBatchSize: 10, Addresses: []string{"127.0.0.1"},
BQueueSize: 20, OIDBatchSize: 10,
SkipIndexFilesSearch: true, BQueueSize: 20,
DownloaderWorkersCount: 4, SkipIndexFilesSearch: true,
DownloaderWorkersCount: 4,
},
}, },
shouldFail: false, shouldFail: false,
}, },
{ {
cfg: NeoFSBlockFetcher{ cfg: ApplicationConfiguration{
InternalService: InternalService{Enabled: true}, NeoFSBlockFetcher: NeoFSBlockFetcher{
Timeout: time.Second, InternalService: InternalService{Enabled: true},
ContainerID: "", Timeout: time.Second,
Addresses: []string{"127.0.0.1"}, ContainerID: "",
OIDBatchSize: 10, Addresses: []string{"127.0.0.1"},
BQueueSize: 20, OIDBatchSize: 10,
BQueueSize: 20,
},
}, },
shouldFail: true, shouldFail: true,
errMsg: "container ID is not set", errMsg: "container ID is not set",
}, },
{ {
cfg: NeoFSBlockFetcher{ cfg: ApplicationConfiguration{
InternalService: InternalService{Enabled: true}, NeoFSBlockFetcher: NeoFSBlockFetcher{
Timeout: time.Second, InternalService: InternalService{Enabled: true},
ContainerID: invalidContainerID, Timeout: time.Second,
Addresses: []string{"127.0.0.1"}, ContainerID: invalidContainerID,
OIDBatchSize: 10, Addresses: []string{"127.0.0.1"},
BQueueSize: 20, OIDBatchSize: 10,
BQueueSize: 20,
},
}, },
shouldFail: true, shouldFail: true,
errMsg: "invalid container ID", errMsg: "invalid container ID",
}, },
{ {
cfg: NeoFSBlockFetcher{ cfg: ApplicationConfiguration{
InternalService: InternalService{Enabled: true}, NeoFSBlockFetcher: NeoFSBlockFetcher{
Timeout: time.Second, InternalService: InternalService{Enabled: true},
ContainerID: validContainerID, Timeout: time.Second,
Addresses: []string{}, ContainerID: validContainerID,
OIDBatchSize: 10, Addresses: []string{},
BQueueSize: 20, OIDBatchSize: 10,
BQueueSize: 20,
},
}, },
shouldFail: true, shouldFail: true,
errMsg: "addresses are not set", errMsg: "addresses are not set",
}, },
{ {
cfg: NeoFSBlockFetcher{ cfg: ApplicationConfiguration{
InternalService: InternalService{Enabled: true}, NeoFSBlockFetcher: NeoFSBlockFetcher{
Timeout: time.Second, InternalService: InternalService{Enabled: true},
ContainerID: validContainerID, Timeout: time.Second,
Addresses: []string{"127.0.0.1"}, ContainerID: validContainerID,
OIDBatchSize: 10, Addresses: []string{"127.0.0.1"},
BQueueSize: 5, OIDBatchSize: 10,
BQueueSize: 5,
},
}, },
shouldFail: true, shouldFail: true,
errMsg: "BQueueSize (5) is lower than OIDBatchSize (10)", errMsg: "BQueueSize (5) is lower than OIDBatchSize (10)",
}, },
{
cfg: ApplicationConfiguration{
Logger: Logger{
LogEncoding: "unknown",
},
},
shouldFail: true,
errMsg: "invalid logger config: invalid LogEncoding: unknown",
},
} }
for _, c := range cases { for _, c := range cases {

20
pkg/config/logger.go Normal file
View file

@ -0,0 +1,20 @@
package config
import (
"fmt"
)
// Logger contains node logger configuration.
type Logger struct {
LogEncoding string `yaml:"LogEncoding"`
LogLevel string `yaml:"LogLevel"`
LogPath string `yaml:"LogPath"`
}
// Validate returns an error if Logger configuration is not valid.
func (l Logger) Validate() error {
if len(l.LogEncoding) > 0 && l.LogEncoding != "console" && l.LogEncoding != "json" {
return fmt.Errorf("invalid LogEncoding: %s", l.LogEncoding)
}
return nil
}