From 5032b224bce25c2ecc630cd56833bab45795fb37 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 23 May 2017 00:43:12 +0200 Subject: [PATCH 1/4] copy log level parsing from tendemint API change due to me not wanting `flags` package to depend on tendermint's config package. Refs https://github.com/tendermint/tendermint/issues/504 --- cli/flags/log_level.go | 86 +++++++++++++++++++++++++++++++++++++ cli/flags/log_level_test.go | 68 +++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 cli/flags/log_level.go create mode 100644 cli/flags/log_level_test.go diff --git a/cli/flags/log_level.go b/cli/flags/log_level.go new file mode 100644 index 000000000..ee4825cf7 --- /dev/null +++ b/cli/flags/log_level.go @@ -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 +} diff --git a/cli/flags/log_level_test.go b/cli/flags/log_level_test.go new file mode 100644 index 000000000..027249145 --- /dev/null +++ b/cli/flags/log_level_test.go @@ -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) + } + } +} From de02488778f3636ded32ca10e4ed9afbe8616481 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 23 May 2017 23:04:33 +0200 Subject: [PATCH 2/4] Enhance the tests to make it clearer how these levels work together --- cli/flags/log_level_test.go | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/cli/flags/log_level_test.go b/cli/flags/log_level_test.go index 027249145..91f49b69c 100644 --- a/cli/flags/log_level_test.go +++ b/cli/flags/log_level_test.go @@ -21,12 +21,22 @@ func TestParseLogLevel(t *testing.T) { lvl string expectedLogLines []string }{ - {"mempool:error", []string{``, ``, `{"_msg":"Mesmero","level":"error","module":"mempool"}`}}, - {"mempool:error,*:debug", []string{``, ``, `{"_msg":"Mesmero","level":"error","module":"mempool"}`}}, + {"mempool:error", []string{ + ``, // if no default is given, assume info + ``, + `{"_msg":"Mesmero","level":"error","module":"mempool"}`, + `{"_msg":"Mind","level":"info","module":"state"}`}}, // if no default is given, assume info + {"mempool:error,*:debug", []string{ + `{"_msg":"Kingpin","level":"debug","module":"wire"}`, + ``, + `{"_msg":"Mesmero","level":"error","module":"mempool"}`, + `{"_msg":"Mind","level":"info","module":"state"}`}}, + {"*:debug,wire:none", []string{ - `{"_msg":"Kingpin","level":"debug","module":"mempool"}`, + ``, `{"_msg":"Kitty Pryde","level":"info","module":"mempool"}`, - `{"_msg":"Mesmero","level":"error","module":"mempool"}`}}, + `{"_msg":"Mesmero","level":"error","module":"mempool"}`, + `{"_msg":"Mind","level":"info","module":"state"}`}}, } for _, c := range correctLogLevels { @@ -35,28 +45,36 @@ func TestParseLogLevel(t *testing.T) { t.Fatal(err) } - logger = logger.With("module", "mempool") + logger = logger buf.Reset() - logger.Debug("Kingpin") + logger.With("module", "wire").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") + logger.With("module", "mempool").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") + logger.With("module", "mempool").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) } + + buf.Reset() + + logger.With("module", "state").Info("Mind") + if have := strings.TrimSpace(buf.String()); c.expectedLogLines[3] != have { + t.Errorf("\nwant '%s'\nhave '%s'\nlevel '%s'", c.expectedLogLines[3], have, c.lvl) + } + } incorrectLogLevel := []string{"some", "mempool:some", "*:some,mempool:error"} From b36203bb02857ce7c2dac66244ec9a2760c650d9 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 23 May 2017 23:27:26 +0200 Subject: [PATCH 3/4] [cli] add a test case to TestParseLogLevel where there is no module key --- cli/flags/log_level_test.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/cli/flags/log_level_test.go b/cli/flags/log_level_test.go index 91f49b69c..458a9e24d 100644 --- a/cli/flags/log_level_test.go +++ b/cli/flags/log_level_test.go @@ -25,18 +25,22 @@ func TestParseLogLevel(t *testing.T) { ``, // if no default is given, assume info ``, `{"_msg":"Mesmero","level":"error","module":"mempool"}`, - `{"_msg":"Mind","level":"info","module":"state"}`}}, // if no default is given, assume info + `{"_msg":"Mind","level":"info","module":"state"}`, // if no default is given, assume info + ``}}, + {"mempool:error,*:debug", []string{ `{"_msg":"Kingpin","level":"debug","module":"wire"}`, ``, `{"_msg":"Mesmero","level":"error","module":"mempool"}`, - `{"_msg":"Mind","level":"info","module":"state"}`}}, + `{"_msg":"Mind","level":"info","module":"state"}`, + `{"_msg":"Gideon","level":"debug"}`}}, {"*:debug,wire:none", []string{ ``, `{"_msg":"Kitty Pryde","level":"info","module":"mempool"}`, `{"_msg":"Mesmero","level":"error","module":"mempool"}`, - `{"_msg":"Mind","level":"info","module":"state"}`}}, + `{"_msg":"Mind","level":"info","module":"state"}`, + `{"_msg":"Gideon","level":"debug"}`}}, } for _, c := range correctLogLevels { @@ -75,6 +79,12 @@ func TestParseLogLevel(t *testing.T) { t.Errorf("\nwant '%s'\nhave '%s'\nlevel '%s'", c.expectedLogLines[3], have, c.lvl) } + buf.Reset() + + logger.Debug("Gideon") + if have := strings.TrimSpace(buf.String()); c.expectedLogLines[4] != have { + t.Errorf("\nwant '%s'\nhave '%s'\nlevel '%s'", c.expectedLogLines[4], have, c.lvl) + } } incorrectLogLevel := []string{"some", "mempool:some", "*:some,mempool:error"} From 4ef77c008ced1f9518bffb2631a692230dd4f920 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 2 Jun 2017 11:55:43 +0300 Subject: [PATCH 4/4] update changelog --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab193688f..a97aa1285 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.2.1 (June 2, 2017) + +FEATURES: + +- [cli] Log level parsing moved here from tendermint repo + ## 0.2.0 (May 18, 2017) BREAKING CHANGES: @@ -14,7 +20,7 @@ FEATURES: BUG FIXES: -- [autofile] Close file before rotating +- [autofile] Close file before rotating ## 0.1.0 (May 1, 2017)