diff --git a/cmd/frostfs-ir/config.go b/cmd/frostfs-ir/config.go
index 09af08525..614811e79 100644
--- a/cmd/frostfs-ir/config.go
+++ b/cmd/frostfs-ir/config.go
@@ -4,11 +4,14 @@ import (
"context"
"os"
"os/signal"
+ "strconv"
"syscall"
configViper "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
+ "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
+ "github.com/spf13/cast"
"github.com/spf13/viper"
"go.uber.org/zap"
)
@@ -38,13 +41,33 @@ func reloadConfig() error {
}
cmode.Store(cfg.GetBool("node.kludge_compatibility_mode"))
audit.Store(cfg.GetBool("audit.enabled"))
+ var logPrm logger.Prm
err = logPrm.SetLevelString(cfg.GetString("logger.level"))
if err != nil {
return err
}
- logPrm.PrependTimestamp = cfg.GetBool("logger.timestamp")
+ err = logPrm.SetTags(loggerTags())
+ if err != nil {
+ return err
+ }
+ log.Reload(logPrm)
- return logPrm.Reload()
+ return nil
+}
+
+func loggerTags() [][]string {
+ var res [][]string
+ for i := 0; ; i++ {
+ var item []string
+ index := strconv.FormatInt(int64(i), 10)
+ names := cast.ToString(cfg.Get("logger.tags." + index + ".names"))
+ if names == "" {
+ break
+ }
+ item = append(item, names, cast.ToString(cfg.Get("logger.tags."+index+".level")))
+ res = append(res, item)
+ }
+ return res
}
func watchForSignal(ctx context.Context, cancel func()) {
diff --git a/cmd/frostfs-ir/main.go b/cmd/frostfs-ir/main.go
index ade64ba84..27a5934f6 100644
--- a/cmd/frostfs-ir/main.go
+++ b/cmd/frostfs-ir/main.go
@@ -31,7 +31,6 @@ const (
var (
wg = new(sync.WaitGroup)
intErr = make(chan error) // internal inner ring errors
- logPrm = new(logger.Prm)
innerRing *innerring.Server
pprofCmp *pprofComponent
metricsCmp *httpComponent
@@ -70,6 +69,7 @@ func main() {
metrics := irMetrics.NewInnerRingMetrics()
+ var logPrm logger.Prm
err = logPrm.SetLevelString(
cfg.GetString("logger.level"),
)
@@ -80,6 +80,8 @@ func main() {
exitErr(err)
logPrm.SamplingHook = metrics.LogMetrics().GetSamplingHook()
logPrm.PrependTimestamp = cfg.GetBool("logger.timestamp")
+ err = logPrm.SetTags(loggerTags())
+ exitErr(err)
log, err = logger.NewLogger(logPrm)
exitErr(err)
diff --git a/cmd/frostfs-node/config.go b/cmd/frostfs-node/config.go
index 2531e9173..61f89b6f8 100644
--- a/cmd/frostfs-node/config.go
+++ b/cmd/frostfs-node/config.go
@@ -108,6 +108,7 @@ type applicationConfiguration struct {
level string
destination string
timestamp bool
+ tags [][]string
}
ObjectCfg struct {
@@ -232,6 +233,7 @@ func (a *applicationConfiguration) readConfig(c *config.Config) error {
a.LoggerCfg.level = loggerconfig.Level(c)
a.LoggerCfg.destination = loggerconfig.Destination(c)
a.LoggerCfg.timestamp = loggerconfig.Timestamp(c)
+ a.LoggerCfg.tags = loggerconfig.Tags(c)
// Object
@@ -473,7 +475,6 @@ type shared struct {
// dynamicConfiguration stores parameters of the
// components that supports runtime reconfigurations.
type dynamicConfiguration struct {
- logger *logger.Prm
pprof *httpComponent
metrics *httpComponent
}
@@ -714,7 +715,8 @@ func initCfg(appCfg *config.Config) *cfg {
netState.metrics = c.metricsCollector
- logPrm := c.loggerPrm()
+ logPrm, err := c.loggerPrm()
+ fatalOnErr(err)
logPrm.SamplingHook = c.metricsCollector.LogMetrics().GetSamplingHook()
log, err := logger.NewLogger(logPrm)
fatalOnErr(err)
@@ -1076,26 +1078,27 @@ func (c *cfg) getShardOpts(ctx context.Context, shCfg shardCfg) shardOptsWithID
return sh
}
-func (c *cfg) loggerPrm() *logger.Prm {
- // check if it has been inited before
- if c.dynamicConfiguration.logger == nil {
- c.dynamicConfiguration.logger = new(logger.Prm)
- }
-
+func (c *cfg) loggerPrm() (logger.Prm, error) {
+ var prm logger.Prm
// (re)init read configuration
- err := c.dynamicConfiguration.logger.SetLevelString(c.LoggerCfg.level)
+ err := prm.SetLevelString(c.LoggerCfg.level)
if err != nil {
// not expected since validation should be performed before
- panic("incorrect log level format: " + c.LoggerCfg.level)
+ return logger.Prm{}, errors.New("incorrect log level format: " + c.LoggerCfg.level)
}
- err = c.dynamicConfiguration.logger.SetDestination(c.LoggerCfg.destination)
+ err = prm.SetDestination(c.LoggerCfg.destination)
if err != nil {
// not expected since validation should be performed before
- panic("incorrect log destination format: " + c.LoggerCfg.destination)
+ return logger.Prm{}, errors.New("incorrect log destination format: " + c.LoggerCfg.destination)
+ }
+ prm.PrependTimestamp = c.LoggerCfg.timestamp
+ err = prm.SetTags(c.LoggerCfg.tags)
+ if err != nil {
+ // not expected since validation should be performed before
+ return logger.Prm{}, errors.New("incorrect allowed tags format: " + c.LoggerCfg.destination)
}
- c.dynamicConfiguration.logger.PrependTimestamp = c.LoggerCfg.timestamp
- return c.dynamicConfiguration.logger
+ return prm, nil
}
func (c *cfg) LocalAddress() network.AddressGroup {
@@ -1335,11 +1338,7 @@ func (c *cfg) reloadConfig(ctx context.Context) {
// all the components are expected to support
// Logger's dynamic reconfiguration approach
- // Logger
-
- logPrm := c.loggerPrm()
-
- components := c.getComponents(ctx, logPrm)
+ components := c.getComponents(ctx)
// Object
c.cfgObject.tombstoneLifetime.Store(c.ObjectCfg.tombstoneLifetime)
@@ -1377,10 +1376,17 @@ func (c *cfg) reloadConfig(ctx context.Context) {
c.log.Info(ctx, logs.FrostFSNodeConfigurationHasBeenReloadedSuccessfully)
}
-func (c *cfg) getComponents(ctx context.Context, logPrm *logger.Prm) []dCmp {
+func (c *cfg) getComponents(ctx context.Context) []dCmp {
var components []dCmp
- components = append(components, dCmp{"logger", logPrm.Reload})
+ components = append(components, dCmp{"logger", func() error {
+ prm, err := c.loggerPrm()
+ if err != nil {
+ return err
+ }
+ c.log.Reload(prm)
+ return nil
+ }})
components = append(components, dCmp{"runtime", func() error {
setRuntimeParameters(ctx, c)
return nil
diff --git a/cmd/frostfs-node/config/logger/config.go b/cmd/frostfs-node/config/logger/config.go
index ba9eeea2b..8e31e34c3 100644
--- a/cmd/frostfs-node/config/logger/config.go
+++ b/cmd/frostfs-node/config/logger/config.go
@@ -2,6 +2,7 @@ package loggerconfig
import (
"os"
+ "strconv"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
@@ -60,6 +61,23 @@ func Timestamp(c *config.Config) bool {
return config.BoolSafe(c.Sub(subsection), "timestamp")
}
+// Tags returns the value of "tags" config parameter from "logger" section.
+func Tags(c *config.Config) [][]string {
+ var res [][]string
+ sub := c.Sub(subsection).Sub("tags")
+ for i := 0; ; i++ {
+ var item []string
+ s := sub.Sub(strconv.FormatInt(int64(i), 10))
+ names := config.StringSafe(s, "names")
+ if names == "" {
+ break
+ }
+ item = append(item, names, config.StringSafe(s, "level"))
+ res = append(res, item)
+ }
+ return res
+}
+
// ToLokiConfig extracts loki config.
func ToLokiConfig(c *config.Config) loki.Config {
hostname, _ := os.Hostname()
diff --git a/cmd/frostfs-node/config/logger/config_test.go b/cmd/frostfs-node/config/logger/config_test.go
index ffe8ac693..796ad529e 100644
--- a/cmd/frostfs-node/config/logger/config_test.go
+++ b/cmd/frostfs-node/config/logger/config_test.go
@@ -22,6 +22,9 @@ func TestLoggerSection_Level(t *testing.T) {
require.Equal(t, "debug", loggerconfig.Level(c))
require.Equal(t, "journald", loggerconfig.Destination(c))
require.Equal(t, true, loggerconfig.Timestamp(c))
+ tags := loggerconfig.Tags(c)
+ require.Equal(t, "main, morph", tags[0][0])
+ require.Equal(t, "debug", tags[0][1])
}
configtest.ForEachFileType(path, fileConfigTest)
diff --git a/cmd/frostfs-node/morph.go b/cmd/frostfs-node/morph.go
index 657e22389..6e1a15f44 100644
--- a/cmd/frostfs-node/morph.go
+++ b/cmd/frostfs-node/morph.go
@@ -14,6 +14,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
netmapEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/subscriber"
+ "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/rand"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/state"
@@ -83,7 +84,7 @@ func initMorphClient(ctx context.Context, c *cfg) {
cli, err := client.New(ctx,
c.key,
client.WithDialTimeout(morphconfig.DialTimeout(c.appCfg)),
- client.WithLogger(c.log),
+ client.WithLogger(c.log.WithTag(logger.TagMorph)),
client.WithMetrics(c.metricsCollector.MorphClientMetrics()),
client.WithEndpoints(addresses...),
client.WithConnLostCallback(func() {
@@ -164,6 +165,7 @@ func listenMorphNotifications(ctx context.Context, c *cfg) {
err error
subs subscriber.Subscriber
)
+ log := c.log.WithTag(logger.TagMorph)
fromSideChainBlock, err := c.persistate.UInt32(persistateSideChainLastBlockKey)
if err != nil {
@@ -172,14 +174,14 @@ func listenMorphNotifications(ctx context.Context, c *cfg) {
}
subs, err = subscriber.New(ctx, &subscriber.Params{
- Log: c.log,
+ Log: log,
StartFromBlock: fromSideChainBlock,
Client: c.cfgMorph.client,
})
fatalOnErr(err)
lis, err := event.NewListener(event.ListenerParams{
- Logger: c.log,
+ Logger: log,
Subscriber: subs,
})
fatalOnErr(err)
@@ -197,7 +199,7 @@ func listenMorphNotifications(ctx context.Context, c *cfg) {
setNetmapNotificationParser(c, newEpochNotification, func(src *state.ContainedNotificationEvent) (event.Event, error) {
res, err := netmapEvent.ParseNewEpoch(src)
if err == nil {
- c.log.Info(ctx, logs.FrostFSNodeNewEpochEventFromSidechain,
+ log.Info(ctx, logs.FrostFSNodeNewEpochEventFromSidechain,
zap.Uint64("number", res.(netmapEvent.NewEpoch).EpochNumber()),
)
}
@@ -208,11 +210,11 @@ func listenMorphNotifications(ctx context.Context, c *cfg) {
registerNotificationHandlers(c.cfgContainer.scriptHash, lis, c.cfgContainer.parsers, c.cfgContainer.subscribers)
registerBlockHandler(lis, func(ctx context.Context, block *block.Block) {
- c.log.Debug(ctx, logs.FrostFSNodeNewBlock, zap.Uint32("index", block.Index))
+ log.Debug(ctx, logs.FrostFSNodeNewBlock, zap.Uint32("index", block.Index))
err = c.persistate.SetUInt32(persistateSideChainLastBlockKey, block.Index)
if err != nil {
- c.log.Warn(ctx, logs.FrostFSNodeCantUpdatePersistentState,
+ log.Warn(ctx, logs.FrostFSNodeCantUpdatePersistentState,
zap.String("chain", "side"),
zap.Uint32("block_index", block.Index))
}
diff --git a/cmd/frostfs-node/session.go b/cmd/frostfs-node/session.go
index 2f3c9cbfe..fbfe3f5e6 100644
--- a/cmd/frostfs-node/session.go
+++ b/cmd/frostfs-node/session.go
@@ -14,6 +14,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/session/storage"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/session/storage/persistent"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/session/storage/temporary"
+ "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session"
sessionGRPC "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session/grpc"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
@@ -55,7 +56,7 @@ func initSessionService(c *cfg) {
server := sessionTransportGRPC.New(
sessionSvc.NewSignService(
&c.key.PrivateKey,
- sessionSvc.NewExecutionService(c.privateTokenStore, c.respSvc, c.log),
+ sessionSvc.NewExecutionService(c.privateTokenStore, c.respSvc, c.log.WithTag(logger.TagSessionSvc)),
),
)
diff --git a/cmd/frostfs-node/tree.go b/cmd/frostfs-node/tree.go
index 65414f0ca..9a86591a3 100644
--- a/cmd/frostfs-node/tree.go
+++ b/cmd/frostfs-node/tree.go
@@ -14,6 +14,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/tree"
+ "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"go.uber.org/zap"
"google.golang.org/grpc"
@@ -56,7 +57,7 @@ func initTreeService(c *cfg) {
tree.WithFrostfsidSubjectProvider(c.shared.frostfsidClient),
tree.WithNetmapSource(c.netMapSource),
tree.WithPrivateKey(&c.key.PrivateKey),
- tree.WithLogger(c.log),
+ tree.WithLogger(c.log.WithTag(logger.TagTreeSvc)),
tree.WithStorage(c.cfgObject.cfgLocalStorage.localStorage),
tree.WithContainerCacheSize(treeConfig.CacheSize()),
tree.WithReplicationTimeout(treeConfig.ReplicationTimeout()),
diff --git a/cmd/frostfs-node/validate.go b/cmd/frostfs-node/validate.go
index ae52b9e4a..22d2e0aa9 100644
--- a/cmd/frostfs-node/validate.go
+++ b/cmd/frostfs-node/validate.go
@@ -30,6 +30,11 @@ func validateConfig(c *config.Config) error {
return fmt.Errorf("invalid logger destination: %w", err)
}
+ err = loggerPrm.SetTags(loggerconfig.Tags(c))
+ if err != nil {
+ return fmt.Errorf("invalid list of allowed tags: %w", err)
+ }
+
// shard configuration validation
shardNum := 0
diff --git a/config/example/ir.env b/config/example/ir.env
index ebd91c243..c13044a6e 100644
--- a/config/example/ir.env
+++ b/config/example/ir.env
@@ -1,5 +1,7 @@
FROSTFS_IR_LOGGER_LEVEL=info
FROSTFS_IR_LOGGER_TIMESTAMP=true
+FROSTFS_IR_LOGGER_TAGS_0_NAMES="main, morph"
+FROSTFS_IR_LOGGER_TAGS_0_LEVEL="debug"
FROSTFS_IR_WALLET_PATH=/path/to/wallet.json
FROSTFS_IR_WALLET_ADDRESS=NUHtW3eM6a4mmFCgyyr4rj4wygsTKB88XX
diff --git a/config/example/ir.yaml b/config/example/ir.yaml
index 49f9fd324..a4a006550 100644
--- a/config/example/ir.yaml
+++ b/config/example/ir.yaml
@@ -3,6 +3,9 @@
logger:
level: info # Logger level: one of "debug", "info" (default), "warn", "error", "dpanic", "panic", "fatal"
timestamp: true
+ tags:
+ - names: "main, morph" # Possible values: `main`, `morph`, `grpc_svc`, `ir`, `processor`.
+ level: debug
wallet:
path: /path/to/wallet.json # Path to NEP-6 NEO wallet file
diff --git a/config/example/node.env b/config/example/node.env
index 010b6840c..3d0114914 100644
--- a/config/example/node.env
+++ b/config/example/node.env
@@ -1,6 +1,8 @@
FROSTFS_LOGGER_LEVEL=debug
FROSTFS_LOGGER_DESTINATION=journald
FROSTFS_LOGGER_TIMESTAMP=true
+FROSTFS_LOGGER_TAGS_0_NAMES="main, morph"
+FROSTFS_LOGGER_TAGS_0_LEVEL="debug"
FROSTFS_PPROF_ENABLED=true
FROSTFS_PPROF_ADDRESS=localhost:6060
diff --git a/config/example/node.json b/config/example/node.json
index b26c35d2c..9fa181cd2 100644
--- a/config/example/node.json
+++ b/config/example/node.json
@@ -2,7 +2,13 @@
"logger": {
"level": "debug",
"destination": "journald",
- "timestamp": true
+ "timestamp": true,
+ "tags": [
+ {
+ "names": "main, morph",
+ "level": "debug"
+ }
+ ]
},
"pprof": {
"enabled": true,
diff --git a/config/example/node.yaml b/config/example/node.yaml
index 58b687d5c..d26d1fe6a 100644
--- a/config/example/node.yaml
+++ b/config/example/node.yaml
@@ -2,6 +2,9 @@ logger:
level: debug # logger level: one of "debug", "info" (default), "warn", "error", "dpanic", "panic", "fatal"
destination: journald # logger destination: one of "stdout" (default), "journald"
timestamp: true
+ tags:
+ - names: "main, morph"
+ level: debug
systemdnotify:
enabled: true
diff --git a/docs/storage-node-configuration.md b/docs/storage-node-configuration.md
index 51f0a9669..e940a827a 100644
--- a/docs/storage-node-configuration.md
+++ b/docs/storage-node-configuration.md
@@ -111,11 +111,21 @@ Contains logger parameters.
```yaml
logger:
level: info
+ tags:
+ - names: "main, morph"
+ level: debug
```
-| Parameter | Type | Default value | Description |
-|-----------|----------|---------------|---------------------------------------------------------------------------------------------------|
-| `level` | `string` | `info` | Logging level.
Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal` |
+| Parameter | Type | Default value | Description |
+|-----------|-----------------------------------------------|---------------|---------------------------------------------------------------------------------------------------|
+| `level` | `string` | `info` | Logging level.
Possible values: `debug`, `info`, `warn`, `error`, `dpanic`, `panic`, `fatal` |
+| `tags` | list of [tags descriptions](#tags-subsection) | | Array of tags description. |
+
+## `tags` subsection
+| Parameter | Type | Default value | Description |
+|-----------|----------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `names` | `string` | | List of components divided by `,`.
Possible values: `main`, `morph`, `grpc_svc`, `ir`, `processor`, `engine`, `blobovnicza`, `blobstor`, `fstree`, `gc`, `shard`, `writecache`, `deletesvc`, `getsvc`, `searchsvc`, `sessionsvc`, `treesvc`, `policer`, `replicator`. |
+| `level` | `string` | | Logging level for the components from `names`, overrides default logging level. |
# `contracts` section
Contains override values for FrostFS side-chain contract hashes. Most of the time contract
diff --git a/pkg/innerring/initialization.go b/pkg/innerring/initialization.go
index f7b71dbe6..3d236641e 100644
--- a/pkg/innerring/initialization.go
+++ b/pkg/innerring/initialization.go
@@ -50,7 +50,7 @@ func (s *Server) initNetmapProcessor(ctx context.Context, cfg *viper.Viper,
var err error
s.netmapProcessor, err = netmap.New(&netmap.Params{
- Log: s.log,
+ Log: s.log.WithTag(logger.TagProcessor),
Metrics: s.irMetrics,
PoolSize: poolSize,
NetmapClient: netmap.NewNetmapClient(s.netmapClient),
@@ -159,7 +159,7 @@ func (s *Server) createAlphaSync(cfg *viper.Viper, frostfsCli *frostfsClient.Cli
} else {
// create governance processor
governanceProcessor, err := governance.New(&governance.Params{
- Log: s.log,
+ Log: s.log.WithTag(logger.TagProcessor),
Metrics: s.irMetrics,
FrostFSClient: frostfsCli,
AlphabetState: s,
@@ -225,7 +225,7 @@ func (s *Server) initAlphabetProcessor(ctx context.Context, cfg *viper.Viper) er
// create alphabet processor
s.alphabetProcessor, err = alphabet.New(&alphabet.Params{
ParsedWallets: parsedWallets,
- Log: s.log,
+ Log: s.log.WithTag(logger.TagProcessor),
Metrics: s.irMetrics,
PoolSize: poolSize,
AlphabetContracts: s.contracts.alphabet,
@@ -247,7 +247,7 @@ func (s *Server) initContainerProcessor(ctx context.Context, cfg *viper.Viper, c
s.log.Debug(ctx, logs.ContainerContainerWorkerPool, zap.Int("size", poolSize))
// container processor
containerProcessor, err := cont.New(&cont.Params{
- Log: s.log,
+ Log: s.log.WithTag(logger.TagProcessor),
Metrics: s.irMetrics,
PoolSize: poolSize,
AlphabetState: s,
@@ -268,7 +268,7 @@ func (s *Server) initBalanceProcessor(ctx context.Context, cfg *viper.Viper, fro
s.log.Debug(ctx, logs.BalanceBalanceWorkerPool, zap.Int("size", poolSize))
// create balance processor
balanceProcessor, err := balance.New(&balance.Params{
- Log: s.log,
+ Log: s.log.WithTag(logger.TagProcessor),
Metrics: s.irMetrics,
PoolSize: poolSize,
FrostFSClient: frostfsCli,
@@ -291,7 +291,7 @@ func (s *Server) initFrostFSMainnetProcessor(ctx context.Context, cfg *viper.Vip
s.log.Debug(ctx, logs.FrostFSFrostfsWorkerPool, zap.Int("size", poolSize))
frostfsProcessor, err := frostfs.New(&frostfs.Params{
- Log: s.log,
+ Log: s.log.WithTag(logger.TagProcessor),
Metrics: s.irMetrics,
PoolSize: poolSize,
FrostFSContract: s.contracts.frostfs,
@@ -342,7 +342,7 @@ func (s *Server) initGRPCServer(ctx context.Context, cfg *viper.Viper, log *logg
controlSvc := controlsrv.NewAuditService(controlsrv.New(p, s.netmapClient, s.containerClient,
controlsrv.WithAllowedKeys(authKeys),
- ), log, audit)
+ ), log.WithTag(logger.TagGrpcSvc), audit)
grpcControlSrv := grpc.NewServer()
control.RegisterControlServiceServer(grpcControlSrv, controlSvc)
@@ -458,7 +458,7 @@ func (s *Server) initMorph(ctx context.Context, cfg *viper.Viper, errChan chan<-
}
morphChain := &chainParams{
- log: s.log,
+ log: s.log.WithTag(logger.TagMorph),
cfg: cfg,
key: s.key,
name: morphPrefix,
diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go
index ae5661905..3a5137261 100644
--- a/pkg/innerring/innerring.go
+++ b/pkg/innerring/innerring.go
@@ -339,7 +339,7 @@ func New(ctx context.Context, log *logger.Logger, cfg *viper.Viper, errChan chan
) (*Server, error) {
var err error
server := &Server{
- log: log,
+ log: log.WithTag(logger.TagIr),
irMetrics: metrics,
cmode: cmode,
}
diff --git a/pkg/local_object_storage/blobovnicza/blobovnicza.go b/pkg/local_object_storage/blobovnicza/blobovnicza.go
index 08ef8b86c..22987d69d 100644
--- a/pkg/local_object_storage/blobovnicza/blobovnicza.go
+++ b/pkg/local_object_storage/blobovnicza/blobovnicza.go
@@ -110,7 +110,7 @@ func WithFullSizeLimit(lim uint64) Option {
// WithLogger returns an option to specify Blobovnicza's logger.
func WithLogger(l *logger.Logger) Option {
return func(c *cfg) {
- c.log = l.With(zap.String("component", "Blobovnicza"))
+ c.log = l.WithTag(logger.TagBlobovnicza)
}
}
diff --git a/pkg/local_object_storage/blobstor/blobstor.go b/pkg/local_object_storage/blobstor/blobstor.go
index f850f48b4..3aeeb09a4 100644
--- a/pkg/local_object_storage/blobstor/blobstor.go
+++ b/pkg/local_object_storage/blobstor/blobstor.go
@@ -91,7 +91,7 @@ func WithStorages(st []SubStorage) Option {
// WithLogger returns option to specify BlobStor's logger.
func WithLogger(l *logger.Logger) Option {
return func(c *cfg) {
- c.log = l.With(zap.String("component", "BlobStor"))
+ c.log = l.WithTag(logger.TagBlobstor)
}
}
diff --git a/pkg/local_object_storage/blobstor/fstree/option.go b/pkg/local_object_storage/blobstor/fstree/option.go
index 7155ddcbb..bc8c7c585 100644
--- a/pkg/local_object_storage/blobstor/fstree/option.go
+++ b/pkg/local_object_storage/blobstor/fstree/option.go
@@ -4,7 +4,6 @@ import (
"io/fs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
- "go.uber.org/zap"
)
type Option func(*FSTree)
@@ -53,6 +52,6 @@ func WithFileCounter(c FileCounter) Option {
func WithLogger(l *logger.Logger) Option {
return func(f *FSTree) {
- f.log = l.With(zap.String("component", "FSTree"))
+ f.log = l.WithTag(logger.TagFSTree)
}
}
diff --git a/pkg/local_object_storage/engine/engine.go b/pkg/local_object_storage/engine/engine.go
index a915c9bd6..05e839162 100644
--- a/pkg/local_object_storage/engine/engine.go
+++ b/pkg/local_object_storage/engine/engine.go
@@ -224,7 +224,7 @@ func New(opts ...Option) *StorageEngine {
// WithLogger returns option to set StorageEngine's logger.
func WithLogger(l *logger.Logger) Option {
return func(c *cfg) {
- c.log = l
+ c.log = l.WithTag(logger.TagEngine)
}
}
diff --git a/pkg/local_object_storage/shard/gc.go b/pkg/local_object_storage/shard/gc.go
index 32a377cd5..3f329095c 100644
--- a/pkg/local_object_storage/shard/gc.go
+++ b/pkg/local_object_storage/shard/gc.go
@@ -495,7 +495,7 @@ func (s *Shard) collectExpiredTombstones(ctx context.Context, e Event) {
}()
epoch := e.(newEpoch).epoch
- log := s.log.With(zap.Uint64("epoch", epoch))
+ log := s.log.With(zap.Uint64("epoch", epoch)).WithTag(logger.TagGC)
log.Debug(ctx, logs.ShardStartedExpiredTombstonesHandling)
defer log.Debug(ctx, logs.ShardFinishedExpiredTombstonesHandling)
diff --git a/pkg/local_object_storage/shard/shard.go b/pkg/local_object_storage/shard/shard.go
index b9ec05f01..665af6edc 100644
--- a/pkg/local_object_storage/shard/shard.go
+++ b/pkg/local_object_storage/shard/shard.go
@@ -204,8 +204,8 @@ func WithPiloramaOptions(opts ...pilorama.Option) Option {
// WithLogger returns option to set Shard's logger.
func WithLogger(l *logger.Logger) Option {
return func(c *cfg) {
- c.log = l
- c.gcCfg.log = l
+ c.log = l.WithTag(logger.TagShard)
+ c.gcCfg.log = l.WithTag(logger.TagGC)
}
}
diff --git a/pkg/local_object_storage/writecache/options.go b/pkg/local_object_storage/writecache/options.go
index dbbe66c19..e393bb25f 100644
--- a/pkg/local_object_storage/writecache/options.go
+++ b/pkg/local_object_storage/writecache/options.go
@@ -5,7 +5,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/qos"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
- "go.uber.org/zap"
)
// Option represents write-cache configuration option.
@@ -46,7 +45,7 @@ type options struct {
// WithLogger sets logger.
func WithLogger(log *logger.Logger) Option {
return func(o *options) {
- o.log = log.With(zap.String("component", "WriteCache"))
+ o.log = log.WithTag(logger.TagWriteCache)
}
}
diff --git a/pkg/services/object/common/writer/ec_test.go b/pkg/services/object/common/writer/ec_test.go
index 2458e352f..d5eeddf21 100644
--- a/pkg/services/object/common/writer/ec_test.go
+++ b/pkg/services/object/common/writer/ec_test.go
@@ -130,7 +130,7 @@ func TestECWriter(t *testing.T) {
nodeKey, err := keys.NewPrivateKey()
require.NoError(t, err)
- log, err := logger.NewLogger(nil)
+ log, err := logger.NewLogger(logger.Prm{})
require.NoError(t, err)
var n nmKeys
diff --git a/pkg/services/object/delete/service.go b/pkg/services/object/delete/service.go
index 867d3f4ef..008264c0a 100644
--- a/pkg/services/object/delete/service.go
+++ b/pkg/services/object/delete/service.go
@@ -92,6 +92,6 @@ func New(gs *getsvc.Service,
// WithLogger returns option to specify Delete service's logger.
func WithLogger(l *logger.Logger) Option {
return func(c *cfg) {
- c.log = l.With(zap.String("component", "objectSDK.Delete service"))
+ c.log = l.WithTag(logger.TagDeleteSvc)
}
}
diff --git a/pkg/services/object/get/service.go b/pkg/services/object/get/service.go
index 9ec10b5f2..972479aff 100644
--- a/pkg/services/object/get/service.go
+++ b/pkg/services/object/get/service.go
@@ -53,6 +53,6 @@ func New(
// WithLogger returns option to specify Get service's logger.
func WithLogger(l *logger.Logger) Option {
return func(s *Service) {
- s.log = l.With(zap.String("component", "Object.Get service"))
+ s.log = l.WithTag(logger.TagGetSvc)
}
}
diff --git a/pkg/services/object/get/v2/service.go b/pkg/services/object/get/v2/service.go
index fc483b74b..43104703f 100644
--- a/pkg/services/object/get/v2/service.go
+++ b/pkg/services/object/get/v2/service.go
@@ -145,6 +145,6 @@ func (s *Service) Head(ctx context.Context, req *objectV2.HeadRequest) (*objectV
func WithLogger(l *logger.Logger) Option {
return func(c *cfg) {
- c.log = l.With(zap.String("component", "Object.Get V2 service"))
+ c.log = l.WithTag(logger.TagGetSvc)
}
}
diff --git a/pkg/services/object/search/service.go b/pkg/services/object/search/service.go
index e1aeca957..ac6e20bd2 100644
--- a/pkg/services/object/search/service.go
+++ b/pkg/services/object/search/service.go
@@ -94,6 +94,6 @@ func New(e *engine.StorageEngine,
// WithLogger returns option to specify Get service's logger.
func WithLogger(l *logger.Logger) Option {
return func(c *cfg) {
- c.log = l.With(zap.String("component", "Object.Search service"))
+ c.log = l.WithTag(logger.TagSearchSvc)
}
}
diff --git a/pkg/services/policer/policer.go b/pkg/services/policer/policer.go
index 4e8bacfec..a8db97a7d 100644
--- a/pkg/services/policer/policer.go
+++ b/pkg/services/policer/policer.go
@@ -4,9 +4,9 @@ import (
"sync"
"time"
+ "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
lru "github.com/hashicorp/golang-lru/v2"
- "go.uber.org/zap"
)
type objectsInWork struct {
@@ -54,7 +54,7 @@ func New(opts ...Option) *Policer {
opts[i](c)
}
- c.log = c.log.With(zap.String("component", "Object Policer"))
+ c.log = c.log.WithTag(logger.TagPolicer)
cache, err := lru.New[oid.Address, time.Time](int(c.cacheSize))
if err != nil {
diff --git a/pkg/services/replicator/replicator.go b/pkg/services/replicator/replicator.go
index 6910fa5af..14311726c 100644
--- a/pkg/services/replicator/replicator.go
+++ b/pkg/services/replicator/replicator.go
@@ -7,7 +7,6 @@ import (
objectwriter "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/common/writer"
getsvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/get"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
- "go.uber.org/zap"
)
// Replicator represents the utility that replicates
@@ -45,7 +44,7 @@ func New(opts ...Option) *Replicator {
opts[i](c)
}
- c.log = c.log.With(zap.String("component", "Object Replicator"))
+ c.log = c.log.WithTag(logger.TagReplicator)
return &Replicator{
cfg: c,
diff --git a/pkg/services/session/executor.go b/pkg/services/session/executor.go
index 12b221613..f0591de71 100644
--- a/pkg/services/session/executor.go
+++ b/pkg/services/session/executor.go
@@ -33,10 +33,7 @@ func NewExecutionService(exec ServiceExecutor, respSvc *response.Service, l *log
}
func (s *executorSvc) Create(ctx context.Context, req *session.CreateRequest) (*session.CreateResponse, error) {
- s.log.Debug(ctx, logs.ServingRequest,
- zap.String("component", "SessionService"),
- zap.String("request", "Create"),
- )
+ s.log.Debug(ctx, logs.ServingRequest, zap.String("request", "Create"))
respBody, err := s.exec.Create(ctx, req.GetBody())
if err != nil {
diff --git a/pkg/util/logger/log.go b/pkg/util/logger/log.go
index 413b1d9aa..ca3d7f581 100644
--- a/pkg/util/logger/log.go
+++ b/pkg/util/logger/log.go
@@ -6,21 +6,34 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/tracing"
qos "git.frostfs.info/TrueCloudLab/frostfs-qos/tagging"
"go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
)
func (l *Logger) Debug(ctx context.Context, msg string, fields ...zap.Field) {
+ if l.denyLogEntry(zapcore.DebugLevel) {
+ return
+ }
l.z.Debug(msg, appendContext(ctx, fields...)...)
}
func (l *Logger) Info(ctx context.Context, msg string, fields ...zap.Field) {
+ if l.denyLogEntry(zapcore.InfoLevel) {
+ return
+ }
l.z.Info(msg, appendContext(ctx, fields...)...)
}
func (l *Logger) Warn(ctx context.Context, msg string, fields ...zap.Field) {
+ if l.denyLogEntry(zapcore.WarnLevel) {
+ return
+ }
l.z.Warn(msg, appendContext(ctx, fields...)...)
}
func (l *Logger) Error(ctx context.Context, msg string, fields ...zap.Field) {
+ if l.denyLogEntry(zapcore.ErrorLevel) {
+ return
+ }
l.z.Error(msg, appendContext(ctx, fields...)...)
}
@@ -33,3 +46,11 @@ func appendContext(ctx context.Context, fields ...zap.Field) []zap.Field {
}
return fields
}
+
+func (l *Logger) denyLogEntry(level zapcore.Level) bool {
+ tl := l.tl.Load().(map[Tag]zapcore.Level)
+ if lvl, ok := tl[l.t]; ok {
+ return level < lvl
+ }
+ return level < l.lvl.Level()
+}
diff --git a/pkg/util/logger/logger.go b/pkg/util/logger/logger.go
index 2eb5e5538..bc6ea88f2 100644
--- a/pkg/util/logger/logger.go
+++ b/pkg/util/logger/logger.go
@@ -2,6 +2,7 @@ package logger
import (
"fmt"
+ "sync/atomic"
"time"
"git.frostfs.info/TrueCloudLab/zapjournald"
@@ -15,6 +16,12 @@ import (
type Logger struct {
z *zap.Logger
lvl zap.AtomicLevel
+ // Tag used by Logger
+ t Tag
+ // Contains map of Tag to log level, overrides lvl
+ tl *atomic.Value
+ // Parent zap.Logger, required to override field zapTagFieldName in the output
+ pz *zap.Logger
}
// Prm groups Logger's parameters.
@@ -23,16 +30,8 @@ type Logger struct {
// Parameters that have been connected to the Logger support its
// configuration changing.
//
-// Passing Prm after a successful connection via the NewLogger, connects
-// the Prm to a new instance of the Logger.
-//
-// See also Reload, SetLevelString.
+// See also Logger.Reload, SetLevelString.
type Prm struct {
- // link to the created Logger
- // instance; used for a runtime
- // reconfiguration
- _log *Logger
-
// support runtime rereading
level zapcore.Level
@@ -44,12 +43,17 @@ type Prm struct {
// PrependTimestamp specifies whether to prepend a timestamp in the log
PrependTimestamp bool
+
+ // map of tag's bit masks to log level, overrides lvl
+ tl map[Tag]zapcore.Level
}
const (
DestinationUndefined = ""
DestinationStdout = "stdout"
DestinationJournald = "journald"
+
+ zapTagFieldName = "tag"
)
// SetLevelString sets the minimum logging level. Default is
@@ -73,20 +77,10 @@ func (p *Prm) SetDestination(d string) error {
return nil
}
-// Reload reloads configuration of a connected instance of the Logger.
-// Returns ErrLoggerNotConnected if no connection has been performed.
-// Returns any reconfiguration error from the Logger directly.
-func (p Prm) Reload() error {
- if p._log == nil {
- // incorrect logger usage
- panic("parameters are not connected to any Logger")
- }
-
- return p._log.reload(p)
-}
-
-func defaultPrm() *Prm {
- return new(Prm)
+// SetTags parses list of tags with log level.
+func (p *Prm) SetTags(tags [][]string) (err error) {
+ p.tl, err = parseTags(tags)
+ return err
}
// NewLogger constructs a new zap logger instance. Constructing with nil
@@ -100,10 +94,7 @@ func defaultPrm() *Prm {
// - ISO8601 time encoding.
//
// Logger records a stack trace for all messages at or above fatal level.
-func NewLogger(prm *Prm) (*Logger, error) {
- if prm == nil {
- prm = defaultPrm()
- }
+func NewLogger(prm Prm) (*Logger, error) {
switch prm.dest {
case DestinationUndefined, DestinationStdout:
return newConsoleLogger(prm)
@@ -114,11 +105,9 @@ func NewLogger(prm *Prm) (*Logger, error) {
}
}
-func newConsoleLogger(prm *Prm) (*Logger, error) {
- lvl := zap.NewAtomicLevelAt(prm.level)
-
+func newConsoleLogger(prm Prm) (*Logger, error) {
c := zap.NewProductionConfig()
- c.Level = lvl
+ c.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
c.Encoding = "console"
if prm.SamplingHook != nil {
c.Sampling.Hook = prm.SamplingHook
@@ -137,16 +126,19 @@ func newConsoleLogger(prm *Prm) (*Logger, error) {
if err != nil {
return nil, err
}
+ parentZap := *lZap
+ lZap = lZap.With(zap.String(zapTagFieldName, tagToString(TagMain)))
- l := &Logger{z: lZap, lvl: lvl}
- prm._log = l
+ v := atomic.Value{}
+ v.Store(prm.tl)
+
+ l := &Logger{z: lZap, pz: &parentZap, lvl: zap.NewAtomicLevelAt(prm.level), t: TagMain, tl: &v}
return l, nil
}
-func newJournaldLogger(prm *Prm) (*Logger, error) {
- lvl := zap.NewAtomicLevelAt(prm.level)
-
+func newJournaldLogger(prm Prm) (*Logger, error) {
+ lvl := zap.NewAtomicLevelAt(zapcore.DebugLevel)
c := zap.NewProductionConfig()
if prm.SamplingHook != nil {
c.Sampling.Hook = prm.SamplingHook
@@ -179,28 +171,48 @@ func newJournaldLogger(prm *Prm) (*Logger, error) {
samplerOpts...,
)
lZap := zap.New(samplingCore, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)), zap.AddCallerSkip(1))
+ parentZap := *lZap
+ lZap = lZap.With(zap.String(zapTagFieldName, tagToString(TagMain)))
- l := &Logger{z: lZap, lvl: lvl}
- prm._log = l
+ v := atomic.Value{}
+ v.Store(prm.tl)
+
+ l := &Logger{z: lZap, pz: &parentZap, lvl: zap.NewAtomicLevelAt(prm.level), t: TagMain, tl: &v}
return l, nil
}
-func (l *Logger) reload(prm Prm) error {
+func (l *Logger) Reload(prm Prm) {
l.lvl.SetLevel(prm.level)
- return nil
+ l.tl.Store(prm.tl)
}
func (l *Logger) WithOptions(options ...zap.Option) {
l.z = l.z.WithOptions(options...)
+ l.pz = l.pz.WithOptions(options...)
}
func (l *Logger) With(fields ...zap.Field) *Logger {
- return &Logger{z: l.z.With(fields...)}
+ c := *l
+ c.z = l.z.With(fields...)
+ return &c
+}
+
+func (l *Logger) WithTag(tag Tag) *Logger {
+ c := *l
+ c.t = tag
+ c.z = c.pz.With(zap.String(zapTagFieldName, tagToString(tag)))
+ return &c
}
func NewLoggerWrapper(z *zap.Logger) *Logger {
+ tl := &atomic.Value{}
+ tl.Store(make(map[Tag]zapcore.Level))
+
return &Logger{
- z: z.WithOptions(zap.AddCallerSkip(1)),
+ z: z.WithOptions(zap.AddCallerSkip(1)),
+ pz: z.WithOptions(zap.AddCallerSkip(1)),
+ tl: tl,
+ lvl: zap.NewAtomicLevelAt(zapcore.DebugLevel),
}
}
diff --git a/pkg/util/logger/logger_test.go b/pkg/util/logger/logger_test.go
new file mode 100644
index 000000000..45cdbf29e
--- /dev/null
+++ b/pkg/util/logger/logger_test.go
@@ -0,0 +1,78 @@
+package logger
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func BenchmarkLoggerDebug(b *testing.B) {
+ ctx := context.Background()
+ prm := Prm{}
+ require.NoError(b, prm.SetLevelString("debug"))
+ logger, err := NewLogger(prm)
+ require.NoError(b, err)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for range b.N {
+ logger.Debug(ctx, "test debug")
+ logger.Info(ctx, "test info")
+ logger.Warn(ctx, "test warn")
+ logger.Error(ctx, "test error")
+ }
+}
+
+func BenchmarkLoggerError(b *testing.B) {
+ ctx := context.Background()
+ prm := Prm{}
+ require.NoError(b, prm.SetLevelString("error"))
+ logger, err := NewLogger(prm)
+ require.NoError(b, err)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for range b.N {
+ logger.Debug(ctx, "test debug")
+ logger.Info(ctx, "test info")
+ logger.Warn(ctx, "test warn")
+ logger.Error(ctx, "test error")
+ }
+}
+
+func BenchmarkLoggerLevelLess(b *testing.B) {
+ ctx := context.Background()
+ prm := Prm{}
+ require.NoError(b, prm.SetLevelString("info"))
+ require.NoError(b, prm.SetTags([][]string{{"main", "debug"}, {"morph", "debug"}}))
+ logger, err := NewLogger(prm)
+ require.NoError(b, err)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for range b.N {
+ logger.Debug(ctx, "test debug")
+ logger.Info(ctx, "test info")
+ logger.Warn(ctx, "test warn")
+ logger.Error(ctx, "test error")
+ }
+}
+
+func BenchmarkLoggerLevelGreater(b *testing.B) {
+ ctx := context.Background()
+ prm := Prm{}
+ require.NoError(b, prm.SetLevelString("debug"))
+ require.NoError(b, prm.SetTags([][]string{{"main", "error"}, {"morph", "debug"}}))
+ logger, err := NewLogger(prm)
+ require.NoError(b, err)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+ for range b.N {
+ logger.Debug(ctx, "test debug")
+ logger.Info(ctx, "test info")
+ logger.Warn(ctx, "test warn")
+ logger.Error(ctx, "test error")
+ }
+}
diff --git a/pkg/util/logger/logger_test.result b/pkg/util/logger/logger_test.result
new file mode 100644
index 000000000..4c7b60768
--- /dev/null
+++ b/pkg/util/logger/logger_test.result
@@ -0,0 +1,71 @@
+BenchmarkLoggerDebug
+goos: linux
+goarch: amd64
+pkg: git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger
+cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
+ 10000 534.6 ns/op 23 B/op 0 allocs/op
+ 10000 530.9 ns/op 23 B/op 0 allocs/op
+ 10000 667.4 ns/op 23 B/op 0 allocs/op
+ 10000 582.2 ns/op 23 B/op 0 allocs/op
+ 10000 530.7 ns/op 23 B/op 0 allocs/op
+ 10000 666.8 ns/op 23 B/op 0 allocs/op
+ 10000 533.1 ns/op 23 B/op 0 allocs/op
+ 10000 531.8 ns/op 23 B/op 0 allocs/op
+ 10000 529.5 ns/op 22 B/op 0 allocs/op
+ 10000 645.0 ns/op 23 B/op 0 allocs/op
+PASS
+ok git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger 0.069s
+
+BenchmarkLoggerError
+goos: linux
+goarch: amd64
+pkg: git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger
+cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
+ 10000 137.3 ns/op 6 B/op 0 allocs/op
+ 10000 144.2 ns/op 6 B/op 0 allocs/op
+ 10000 137.5 ns/op 6 B/op 0 allocs/op
+ 10000 142.6 ns/op 6 B/op 0 allocs/op
+ 10000 136.3 ns/op 6 B/op 0 allocs/op
+ 10000 137.0 ns/op 6 B/op 0 allocs/op
+ 10000 144.0 ns/op 6 B/op 0 allocs/op
+ 10000 152.6 ns/op 6 B/op 0 allocs/op
+ 10000 136.4 ns/op 6 B/op 0 allocs/op
+ 10000 137.9 ns/op 6 B/op 0 allocs/op
+PASS
+ok git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger 0.024s
+
+BenchmarkLoggerLevelLess
+goos: linux
+goarch: amd64
+pkg: git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger
+cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
+ 10000 554.3 ns/op 23 B/op 0 allocs/op
+ 10000 630.1 ns/op 23 B/op 0 allocs/op
+ 10000 642.0 ns/op 23 B/op 0 allocs/op
+ 10000 548.0 ns/op 23 B/op 0 allocs/op
+ 10000 562.7 ns/op 23 B/op 0 allocs/op
+ 10000 572.7 ns/op 23 B/op 0 allocs/op
+ 10000 552.1 ns/op 23 B/op 0 allocs/op
+ 10000 555.4 ns/op 23 B/op 0 allocs/op
+ 10000 546.0 ns/op 23 B/op 0 allocs/op
+ 10000 561.0 ns/op 22 B/op 0 allocs/op
+PASS
+ok git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger 0.069s
+
+BenchmarkLoggerLevelGreater
+goos: linux
+goarch: amd64
+pkg: git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger
+cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
+ 10000 159.6 ns/op 6 B/op 0 allocs/op
+ 10000 161.9 ns/op 6 B/op 0 allocs/op
+ 10000 161.4 ns/op 6 B/op 0 allocs/op
+ 10000 160.9 ns/op 6 B/op 0 allocs/op
+ 10000 166.9 ns/op 6 B/op 0 allocs/op
+ 10000 163.9 ns/op 6 B/op 0 allocs/op
+ 10000 163.4 ns/op 6 B/op 0 allocs/op
+ 10000 158.7 ns/op 6 B/op 0 allocs/op
+ 10000 173.2 ns/op 6 B/op 0 allocs/op
+ 10000 160.7 ns/op 6 B/op 0 allocs/op
+PASS
+ok git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger 0.028s
diff --git a/pkg/util/logger/tags.go b/pkg/util/logger/tags.go
new file mode 100644
index 000000000..84eab085d
--- /dev/null
+++ b/pkg/util/logger/tags.go
@@ -0,0 +1,177 @@
+package logger
+
+import (
+ "fmt"
+ "math"
+ "strings"
+
+ "go.uber.org/zap/zapcore"
+)
+
+type Tag uint8
+
+const (
+ TagMain Tag = iota
+ TagMorph
+ TagGrpcSvc
+ TagIr
+ TagProcessor
+ TagEngine
+ TagBlobovnicza
+ TagBlobstor
+ TagFSTree
+ TagGC
+ TagShard
+ TagWriteCache
+ TagDeleteSvc
+ TagGetSvc
+ TagSearchSvc
+ TagSessionSvc
+ TagTreeSvc
+ TagPolicer
+ TagReplicator
+
+ tagMain = "main"
+ tagMorph = "morph"
+ tagGrpcSvc = "grpc_svc"
+ tagIr = "ir"
+ tagProcessor = "processor"
+ tagEngine = "engine"
+ tagBlobovnicza = "blobovnicza"
+ tagBlobstor = "blobstor"
+ tagFSTree = "fstree"
+ tagGC = "gc"
+ tagShard = "shard"
+ tagWriteCache = "writecache"
+ tagDeleteSvc = "deletesvc"
+ tagGetSvc = "getsvc"
+ tagSearchSvc = "searchsvc"
+ tagSessionSvc = "sessionsvc"
+ tagTreeSvc = "treesvc"
+ tagPolicer = "policer"
+ tagReplicator = "replicator"
+)
+
+// tagToMask return bit mask for the tag, encoded in uint32.
+func tagFromString(str string) (Tag, error) {
+ switch str {
+ case tagMain:
+ return TagMain, nil
+ case tagMorph:
+ return TagMorph, nil
+ case tagGrpcSvc:
+ return TagGrpcSvc, nil
+ case tagIr:
+ return TagIr, nil
+ case tagProcessor:
+ return TagProcessor, nil
+ case tagEngine:
+ return TagEngine, nil
+ case tagBlobovnicza:
+ return TagBlobovnicza, nil
+ case tagBlobstor:
+ return TagBlobstor, nil
+ case tagFSTree:
+ return TagFSTree, nil
+ case tagGC:
+ return TagGC, nil
+ case tagShard:
+ return TagShard, nil
+ case tagWriteCache:
+ return TagWriteCache, nil
+ case tagDeleteSvc:
+ return TagDeleteSvc, nil
+ case tagGetSvc:
+ return TagGetSvc, nil
+ case tagSearchSvc:
+ return TagSearchSvc, nil
+ case tagSessionSvc:
+ return TagSessionSvc, nil
+ case tagTreeSvc:
+ return TagTreeSvc, nil
+ case tagPolicer:
+ return TagPolicer, nil
+ case tagReplicator:
+ return TagReplicator, nil
+ default:
+ return math.MaxUint8, fmt.Errorf("unsupported tag %s", str)
+ }
+}
+
+// tagToString return string representation for the tag.
+func tagToString(tag Tag) string {
+ switch tag {
+ case TagMain:
+ return tagMain
+ case TagMorph:
+ return tagMorph
+ case TagGrpcSvc:
+ return tagGrpcSvc
+ case TagIr:
+ return tagIr
+ case TagProcessor:
+ return tagProcessor
+ case TagEngine:
+ return tagEngine
+ case TagBlobovnicza:
+ return tagBlobovnicza
+ case TagBlobstor:
+ return tagBlobstor
+ case TagFSTree:
+ return tagFSTree
+ case TagGC:
+ return tagGC
+ case TagShard:
+ return tagShard
+ case TagWriteCache:
+ return tagWriteCache
+ case TagDeleteSvc:
+ return tagDeleteSvc
+ case TagGetSvc:
+ return tagGetSvc
+ case TagSearchSvc:
+ return tagSearchSvc
+ case TagSessionSvc:
+ return tagSessionSvc
+ case TagTreeSvc:
+ return tagTreeSvc
+ case TagPolicer:
+ return tagPolicer
+ case TagReplicator:
+ return tagReplicator
+ default:
+ return ""
+ }
+}
+
+// parseTags returns:
+// - map(always instantiated) of tag to custom log level for that tag;
+// - error if it occurred(map is empty).
+func parseTags(raw [][]string) (map[Tag]zapcore.Level, error) {
+ m := make(map[Tag]zapcore.Level)
+ if len(raw) == 0 {
+ return m, nil
+ }
+ for _, item := range raw {
+ str, level := item[0], item[1]
+ if len(level) == 0 {
+ // It is not necessary to parse tags without level,
+ // because default log level will be used.
+ continue
+ }
+ var l zapcore.Level
+ err := l.UnmarshalText([]byte(level))
+ if err != nil {
+ return nil, err
+ }
+ tmp := strings.Split(str, ",")
+ for _, tagStr := range tmp {
+ tag, err := tagFromString(strings.TrimSpace(tagStr))
+ if err != nil {
+ return nil, err
+ }
+ m[tag] = l
+ }
+ }
+ return m, nil
+}