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) | |||
} | |||
} | |||
} |