Skip to content

Commit

Permalink
Read log levels from flags
Browse files Browse the repository at this point in the history
  • Loading branch information
qdm12 committed Nov 2, 2021
1 parent becec9e commit 4ebae50
Show file tree
Hide file tree
Showing 3 changed files with 298 additions and 84 deletions.
150 changes: 67 additions & 83 deletions cmd/gossamer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package main

import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -287,115 +288,98 @@ func createExportConfig(ctx *cli.Context) (*dot.Config, error) {
return cfg, nil
}

func setLogConfig(ctx *cli.Context, cfg *ctoml.Config, globalCfg *dot.GlobalConfig, logCfg *dot.LogConfig) error {
if cfg == nil {
cfg = new(ctoml.Config)
}

if lvlStr := ctx.String(LogFlag.Name); lvlStr != "" {
if lvlToInt, err := strconv.Atoi(lvlStr); err == nil {
lvlStr = log.Lvl(lvlToInt).String()
}
cfg.Global.LogLvl = lvlStr
}
type getStringer interface {
String(key string) (value string)
}

if cfg.Global.LogLvl == "" {
cfg.Global.LogLvl = gssmr.DefaultLvl.String()
// getLogLevel obtains the log level in the following order:
// 1. Try to obtain it from the flag value corresponding to flagName.
// 2. Try to obtain it from the TOML value given, if step 1. failed.
// 3. Return the default value given if both previous steps failed.
// For steps 1 and 2, it tries to parse the level as an integer to convert it
// to a level, and also tries to parse it as a string.
func getLogLevel(ctx getStringer, flagName, tomlValue string, defaultLevel log.Lvl) (
level log.Lvl, err error) {
if flagValue := ctx.String(flagName); flagValue != "" {
return parseLogLevelString(flagValue)
}

var err error
globalCfg.LogLvl, err = log.LvlFromString(cfg.Global.LogLvl)
if err != nil {
return err
if tomlValue == "" {
return defaultLevel, nil
}

// check and set log levels for each pkg
if cfg.Log.CoreLvl == "" {
logCfg.CoreLvl = globalCfg.LogLvl
} else {
lvl, err := log.LvlFromString(cfg.Log.CoreLvl)
if err != nil {
return err
}
return parseLogLevelString(tomlValue)
}

logCfg.CoreLvl = lvl
}
var regexDigits = regexp.MustCompile("^[0-9]+$")

if cfg.Log.SyncLvl == "" {
logCfg.SyncLvl = globalCfg.LogLvl
} else {
lvl, err := log.LvlFromString(cfg.Log.SyncLvl)
if err != nil {
return err
func parseLogLevelString(logLevelString string) (logLevel log.Lvl, err error) {
if regexDigits.MatchString(logLevelString) {
levelInt, err := strconv.Atoi(logLevelString)
if err != nil { // should never happen
return 0, fmt.Errorf("cannot parse log level digits: %w", err)
}
logLevel = log.Lvl(levelInt)
return logLevel, nil
}

logCfg.SyncLvl = lvl
logLevel, err = log.LvlFromString(logLevelString)
if err != nil {
return 0, fmt.Errorf("cannot parse log level string: %w", err)
}

if cfg.Log.NetworkLvl == "" {
logCfg.NetworkLvl = globalCfg.LogLvl
} else {
lvl, err := log.LvlFromString(cfg.Log.NetworkLvl)
if err != nil {
return err
}
return logLevel, nil
}

logCfg.NetworkLvl = lvl
func setLogConfig(ctx getStringer, cfg *ctoml.Config, globalCfg *dot.GlobalConfig, logCfg *dot.LogConfig) (err error) {
if cfg == nil {
cfg = new(ctoml.Config)
}

if cfg.Log.RPCLvl == "" {
logCfg.RPCLvl = globalCfg.LogLvl
} else {
lvl, err := log.LvlFromString(cfg.Log.RPCLvl)
if err != nil {
return err
}

logCfg.RPCLvl = lvl
globalCfg.LogLvl, err = getLogLevel(ctx, LogFlag.Name, cfg.Global.LogLvl, gssmr.DefaultLvl)
if err != nil {
return fmt.Errorf("cannot get global log level: %w", err)
}
cfg.Global.LogLvl = globalCfg.LogLvl.String()

if cfg.Log.StateLvl == "" {
logCfg.StateLvl = globalCfg.LogLvl
} else {
lvl, err := log.LvlFromString(cfg.Log.StateLvl)
if err != nil {
return err
}
logCfg.CoreLvl, err = getLogLevel(ctx, LogCoreLevelFlag.Name, cfg.Log.CoreLvl, globalCfg.LogLvl)
if err != nil {
return fmt.Errorf("cannot get core log level: %w", err)
}

logCfg.StateLvl = lvl
logCfg.SyncLvl, err = getLogLevel(ctx, LogSyncLevelFlag.Name, cfg.Log.SyncLvl, globalCfg.LogLvl)
if err != nil {
return fmt.Errorf("cannot get sync log level: %w", err)
}

if cfg.Log.RuntimeLvl == "" {
logCfg.RuntimeLvl = globalCfg.LogLvl
} else {
lvl, err := log.LvlFromString(cfg.Log.RuntimeLvl)
if err != nil {
return err
}
logCfg.NetworkLvl, err = getLogLevel(ctx, LogNetworkLevelFlag.Name, cfg.Log.NetworkLvl, globalCfg.LogLvl)
if err != nil {
return fmt.Errorf("cannot get network log level: %w", err)
}

logCfg.RuntimeLvl = lvl
logCfg.RPCLvl, err = getLogLevel(ctx, LogRPCLevelFlag.Name, cfg.Log.RPCLvl, globalCfg.LogLvl)
if err != nil {
return fmt.Errorf("cannot get RPC log level: %w", err)
}

if cfg.Log.BlockProducerLvl == "" {
logCfg.BlockProducerLvl = globalCfg.LogLvl
} else {
lvl, err := log.LvlFromString(cfg.Log.BlockProducerLvl)
if err != nil {
return err
}
logCfg.StateLvl, err = getLogLevel(ctx, LogStateLevelFlag.Name, cfg.Log.StateLvl, globalCfg.LogLvl)
if err != nil {
return fmt.Errorf("cannot get state log level: %w", err)
}

logCfg.BlockProducerLvl = lvl
logCfg.RuntimeLvl, err = getLogLevel(ctx, LogRuntimeLevelFlag.Name, cfg.Log.RuntimeLvl, globalCfg.LogLvl)
if err != nil {
return fmt.Errorf("cannot get runtime log level: %w", err)
}

if cfg.Log.FinalityGadgetLvl == "" {
logCfg.FinalityGadgetLvl = globalCfg.LogLvl
} else {
lvl, err := log.LvlFromString(cfg.Log.FinalityGadgetLvl)
if err != nil {
return err
}
logCfg.BlockProducerLvl, err = getLogLevel(ctx, LogBlockProducerLevelFlag.Name, cfg.Log.BlockProducerLvl, globalCfg.LogLvl)
if err != nil {
return fmt.Errorf("cannot get block producer log level: %w", err)
}

logCfg.FinalityGadgetLvl = lvl
logCfg.FinalityGadgetLvl, err = getLogLevel(ctx, LogFinalityGadgetLevelFlag.Name, cfg.Log.FinalityGadgetLvl, globalCfg.LogLvl)
if err != nil {
return fmt.Errorf("cannot get finality gadget log level: %w", err)
}

logger.Debug("set log configuration", "--log", ctx.String(LogFlag.Name), "global", globalCfg.LogLvl)
Expand Down
181 changes: 181 additions & 0 deletions cmd/gossamer/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,22 @@
package main

import (
"errors"
"io/ioutil"
"testing"
"time"

"github.com/ChainSafe/gossamer/chain/dev"
"github.com/ChainSafe/gossamer/chain/gssmr"
"github.com/ChainSafe/gossamer/dot"
ctoml "github.com/ChainSafe/gossamer/dot/config/toml"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/utils"

log "github.com/ChainSafe/log15"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
Expand Down Expand Up @@ -1063,3 +1066,181 @@ func TestGlobalNodeNamePriorityOrder(t *testing.T) {
require.NotEqual(t, cfg.Global.Name, createdCfg.Global.Name)
})
}

type mockGetStringer struct {
kv map[string]string
}

func (m *mockGetStringer) String(key string) (value string) {
return m.kv[key]
}

func newMockGetStringer(keyValue map[string]string) *mockGetStringer {
kv := make(map[string]string, len(keyValue))
for k, v := range keyValue {
kv[k] = v
}
return &mockGetStringer{kv: kv}
}

func Test_getLogLevel(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
ctx getStringer
flagName string
tomlValue string
defaultLevel log.Lvl
level log.Lvl
err error
}{
"no value with default": {
ctx: newMockGetStringer(map[string]string{}),
defaultLevel: log.LvlError,
level: log.LvlError,
},
"flag integer value": {
ctx: newMockGetStringer(map[string]string{"x": "1"}),
flagName: "x",
level: log.LvlError,
},
"flag string value": {
ctx: newMockGetStringer(map[string]string{"x": "eror"}),
flagName: "x",
level: log.LvlError,
},
"flag bad string value": {
ctx: newMockGetStringer(map[string]string{"x": "garbage"}),
flagName: "x",
err: errors.New("cannot parse log level string: Unknown level: garbage"),
},
"toml integer value": {
ctx: newMockGetStringer(map[string]string{}),
tomlValue: "1",
level: log.LvlError,
},
"toml string value": {
ctx: newMockGetStringer(map[string]string{}),
tomlValue: "eror",
level: log.LvlError,
},
"toml bad string value": {
ctx: newMockGetStringer(map[string]string{}),
tomlValue: "garbage",
err: errors.New("cannot parse log level string: Unknown level: garbage"),
},
"flag takes precedence": {
ctx: newMockGetStringer(map[string]string{"x": "eror"}),
flagName: "x",
tomlValue: "warn",
level: log.LvlError,
},
}

for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()

level, err := getLogLevel(testCase.ctx, testCase.flagName,
testCase.tomlValue, testCase.defaultLevel)

if testCase.err != nil {
assert.EqualError(t, err, testCase.err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.level, level)
})
}
}

func Test_setLogConfig(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
ctx getStringer
initialCfg ctoml.Config
initialGlobalCfg dot.GlobalConfig
initialLogCfg dot.LogConfig
expectedCfg ctoml.Config
expectedGlobalCfg dot.GlobalConfig
expectedLogCfg dot.LogConfig
err error
}{
"no value": {
ctx: newMockGetStringer(map[string]string{}),
expectedCfg: ctoml.Config{
Global: ctoml.GlobalConfig{
LogLvl: log.LvlInfo.String(),
},
},
expectedGlobalCfg: dot.GlobalConfig{
LogLvl: log.LvlInfo,
},
expectedLogCfg: dot.LogConfig{
CoreLvl: log.LvlInfo,
SyncLvl: log.LvlInfo,
NetworkLvl: log.LvlInfo,
RPCLvl: log.LvlInfo,
StateLvl: log.LvlInfo,
RuntimeLvl: log.LvlInfo,
BlockProducerLvl: log.LvlInfo,
FinalityGadgetLvl: log.LvlInfo,
},
},
"some values": {
ctx: newMockGetStringer(map[string]string{}),
initialCfg: ctoml.Config{
Log: ctoml.LogConfig{
CoreLvl: log.LvlError.String(),
SyncLvl: log.LvlDebug.String(),
StateLvl: log.LvlWarn.String(),
},
},
expectedCfg: ctoml.Config{
Global: ctoml.GlobalConfig{
LogLvl: log.LvlInfo.String(),
},
Log: ctoml.LogConfig{
CoreLvl: log.LvlError.String(),
SyncLvl: log.LvlDebug.String(),
StateLvl: log.LvlWarn.String(),
},
},
expectedGlobalCfg: dot.GlobalConfig{
LogLvl: log.LvlInfo,
},
expectedLogCfg: dot.LogConfig{
CoreLvl: log.LvlError,
SyncLvl: log.LvlDebug,
NetworkLvl: log.LvlInfo,
RPCLvl: log.LvlInfo,
StateLvl: log.LvlWarn,
RuntimeLvl: log.LvlInfo,
BlockProducerLvl: log.LvlInfo,
FinalityGadgetLvl: log.LvlInfo,
},
},
}

for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()

err := setLogConfig(testCase.ctx, &testCase.initialCfg,
&testCase.initialGlobalCfg, &testCase.initialLogCfg)

if testCase.err != nil {
assert.EqualError(t, err, testCase.err.Error())
} else {
assert.NoError(t, err)
}

assert.Equal(t, testCase.expectedCfg, testCase.initialCfg)
assert.Equal(t, testCase.expectedGlobalCfg, testCase.initialGlobalCfg)
assert.Equal(t, testCase.expectedLogCfg, testCase.initialLogCfg)
})
}
}
Loading

0 comments on commit 4ebae50

Please sign in to comment.