API change due to me not wanting `flags` package to depend on tendermint's config package. Refs https://github.com/tendermint/tendermint/issues/504pull/1842/head
@ -0,0 +1,86 @@ | |||||
package flags | |||||
import ( | |||||
"fmt" | |||||
"strings" | |||||
"github.com/pkg/errors" | |||||
"github.com/tendermint/tmlibs/log" | |||||
) | |||||
const ( | |||||
defaultLogLevelKey = "*" | |||||
) | |||||
// ParseLogLevel parses complex log level - comma-separated | |||||
// list of module:level pairs with an optional *:level pair (* means | |||||
// all other modules). | |||||
// | |||||
// Example: | |||||
// ParseLogLevel("consensus:debug,mempool:debug,*:error", log.NewTMLogger(os.Stdout), "info") | |||||
func ParseLogLevel(lvl string, logger log.Logger, defaultLogLevelValue string) (log.Logger, error) { | |||||
if lvl == "" { | |||||
return nil, errors.New("Empty log level") | |||||
} | |||||
l := lvl | |||||
// prefix simple one word levels (e.g. "info") with "*" | |||||
if !strings.Contains(l, ":") { | |||||
l = defaultLogLevelKey + ":" + l | |||||
} | |||||
options := make([]log.Option, 0) | |||||
isDefaultLogLevelSet := false | |||||
var option log.Option | |||||
var err error | |||||
list := strings.Split(l, ",") | |||||
for _, item := range list { | |||||
moduleAndLevel := strings.Split(item, ":") | |||||
if len(moduleAndLevel) != 2 { | |||||
return nil, fmt.Errorf("Expected list in a form of \"module:level\" pairs, given pair %s, list %s", item, list) | |||||
} | |||||
module := moduleAndLevel[0] | |||||
level := moduleAndLevel[1] | |||||
if module == defaultLogLevelKey { | |||||
option, err = log.AllowLevel(level) | |||||
if err != nil { | |||||
return nil, errors.Wrap(err, fmt.Sprintf("Failed to parse default log level (pair %s, list %s)", item, l)) | |||||
} | |||||
options = append(options, option) | |||||
isDefaultLogLevelSet = true | |||||
} else { | |||||
switch level { | |||||
case "debug": | |||||
option = log.AllowDebugWith("module", module) | |||||
case "info": | |||||
option = log.AllowInfoWith("module", module) | |||||
case "error": | |||||
option = log.AllowErrorWith("module", module) | |||||
case "none": | |||||
option = log.AllowNoneWith("module", module) | |||||
default: | |||||
return nil, fmt.Errorf("Expected either \"info\", \"debug\", \"error\" or \"none\" log level, given %s (pair %s, list %s)", level, item, list) | |||||
} | |||||
options = append(options, option) | |||||
} | |||||
} | |||||
// if "*" is not provided, set default global level | |||||
if !isDefaultLogLevelSet { | |||||
option, err = log.AllowLevel(defaultLogLevelValue) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
options = append(options, option) | |||||
} | |||||
return log.NewFilter(logger, options...), nil | |||||
} |
@ -0,0 +1,68 @@ | |||||
package flags_test | |||||
import ( | |||||
"bytes" | |||||
"strings" | |||||
"testing" | |||||
tmflags "github.com/tendermint/tmlibs/cli/flags" | |||||
"github.com/tendermint/tmlibs/log" | |||||
) | |||||
const ( | |||||
defaultLogLevelValue = "info" | |||||
) | |||||
func TestParseLogLevel(t *testing.T) { | |||||
var buf bytes.Buffer | |||||
jsonLogger := log.NewTMJSONLogger(&buf) | |||||
correctLogLevels := []struct { | |||||
lvl string | |||||
expectedLogLines []string | |||||
}{ | |||||
{"mempool:error", []string{``, ``, `{"_msg":"Mesmero","level":"error","module":"mempool"}`}}, | |||||
{"mempool:error,*:debug", []string{``, ``, `{"_msg":"Mesmero","level":"error","module":"mempool"}`}}, | |||||
{"*:debug,wire:none", []string{ | |||||
`{"_msg":"Kingpin","level":"debug","module":"mempool"}`, | |||||
`{"_msg":"Kitty Pryde","level":"info","module":"mempool"}`, | |||||
`{"_msg":"Mesmero","level":"error","module":"mempool"}`}}, | |||||
} | |||||
for _, c := range correctLogLevels { | |||||
logger, err := tmflags.ParseLogLevel(c.lvl, jsonLogger, defaultLogLevelValue) | |||||
if err != nil { | |||||
t.Fatal(err) | |||||
} | |||||
logger = logger.With("module", "mempool") | |||||
buf.Reset() | |||||
logger.Debug("Kingpin") | |||||
if have := strings.TrimSpace(buf.String()); c.expectedLogLines[0] != have { | |||||
t.Errorf("\nwant '%s'\nhave '%s'\nlevel '%s'", c.expectedLogLines[0], have, c.lvl) | |||||
} | |||||
buf.Reset() | |||||
logger.Info("Kitty Pryde") | |||||
if have := strings.TrimSpace(buf.String()); c.expectedLogLines[1] != have { | |||||
t.Errorf("\nwant '%s'\nhave '%s'\nlevel '%s'", c.expectedLogLines[1], have, c.lvl) | |||||
} | |||||
buf.Reset() | |||||
logger.Error("Mesmero") | |||||
if have := strings.TrimSpace(buf.String()); c.expectedLogLines[2] != have { | |||||
t.Errorf("\nwant '%s'\nhave '%s'\nlevel '%s'", c.expectedLogLines[2], have, c.lvl) | |||||
} | |||||
} | |||||
incorrectLogLevel := []string{"some", "mempool:some", "*:some,mempool:error"} | |||||
for _, lvl := range incorrectLogLevel { | |||||
if _, err := tmflags.ParseLogLevel(lvl, jsonLogger, defaultLogLevelValue); err == nil { | |||||
t.Fatalf("Expected %s to produce error", lvl) | |||||
} | |||||
} | |||||
} |