From 651e674701f8907a2526f862ecdc83a7c9e899d1 Mon Sep 17 00:00:00 2001 From: Antoine Grondin Date: Wed, 14 Sep 2022 21:33:41 -0400 Subject: [PATCH] make the tests work by doing config properly --- cmd/humanlog/main.go | 27 +-- color.go | 116 +++++++++++ e2e_test.go | 13 +- go.mod | 2 +- handler.go | 61 ++++++ .../pkg/config}/config.go | 184 +----------------- json_handler_test.go | 2 +- .../00001-json/{opts.json => config.json} | 0 .../00002-logfmt/{opts.json => config.json} | 0 .../00003-zap/{opts.json => config.json} | 0 .../00004-mixed/{opts.json => config.json} | 0 .../{opts.json => config.json} | 0 .../{opts.json => config.json} | 0 .../{opts.json => config.json} | 0 .../{opts.json => config.json} | 0 .../{opts.json => config.json} | 0 16 files changed, 207 insertions(+), 198 deletions(-) rename {cmd/humanlog => internal/pkg/config}/config.go (51%) rename test/cases/00001-json/{opts.json => config.json} (100%) rename test/cases/00002-logfmt/{opts.json => config.json} (100%) rename test/cases/00003-zap/{opts.json => config.json} (100%) rename test/cases/00004-mixed/{opts.json => config.json} (100%) rename test/cases/10000-behavior-base/{opts.json => config.json} (100%) rename test/cases/10001-behavior-truncates/{opts.json => config.json} (100%) rename test/cases/20001-strip-doker-compose/{opts.json => config.json} (100%) rename test/cases/20001-strip-syslog/{opts.json => config.json} (100%) rename test/cases/90000-misc-unchanged/{opts.json => config.json} (100%) diff --git a/cmd/humanlog/main.go b/cmd/humanlog/main.go index b5df247..ded03aa 100644 --- a/cmd/humanlog/main.go +++ b/cmd/humanlog/main.go @@ -6,9 +6,10 @@ import ( "os" "os/signal" - "github.com/aybabtme/humanlog" "github.com/aybabtme/rgbterm" "github.com/fatih/color" + "github.com/humanlogio/humanlog" + "github.com/humanlogio/humanlog/internal/pkg/config" "github.com/mattn/go-colorable" "github.com/urfave/cli" ) @@ -74,13 +75,13 @@ func newApp() *cli.App { truncateLength := cli.IntFlag{ Name: "truncate-length", Usage: "truncate values that are longer than this length", - Value: *defaultConfig.TruncateLength, + Value: *config.DefaultConfig.TruncateLength, } colorFlag := cli.StringFlag{ Name: "color", Usage: "specify color mode: auto, on/force, off", - Value: *defaultConfig.ColorFlag, + Value: *config.DefaultConfig.ColorFlag, } lightBg := cli.BoolFlag{ @@ -134,21 +135,21 @@ func newApp() *cli.App { app.Action = func(c *cli.Context) error { - configFilepath, err := getDefaultConfigFilepath() + configFilepath, err := config.GetDefaultConfigFilepath() if err != nil { return fmt.Errorf("looking up config file path: %v", err) } // read config - var cfg *Config + var cfg *config.Config if c.IsSet(configFlag.Name) { configFilepath = c.String(configFlag.Name) - cfgFromFlag, err := readConfigFile(configFilepath, defaultConfig) + cfgFromFlag, err := config.ReadConfigFile(configFilepath, &config.DefaultConfig) if err != nil { return fmt.Errorf("reading --config file %q: %v", configFilepath, err) } cfg = cfgFromFlag } else { - cfgFromDir, err := readConfigFile(configFilepath, defaultConfig) + cfgFromDir, err := config.ReadConfigFile(configFilepath, &config.DefaultConfig) if err != nil { return fmt.Errorf("reading default config file: %v", err) } @@ -205,14 +206,14 @@ func newApp() *cli.App { signal.Ignore(os.Interrupt) } - colorMode, err := GrokColorMode(*cfg.ColorFlag) + colorMode, err := config.GrokColorMode(*cfg.ColorFlag) if err != nil { return fmt.Errorf("invalid --color=%q: %v", *cfg.ColorFlag, err) } switch colorMode { - case ColorModeOff: + case config.ColorModeOff: color.NoColor = true - case ColorModeOn: + case config.ColorModeOn: color.NoColor = false default: // 'Auto' default is applied as a global variable initializer function, so nothing @@ -223,7 +224,7 @@ func newApp() *cli.App { fatalf(c, "can only use one of %q and %q", skipFlag.Name, keepFlag.Name) } - opts := cfg.toHandlerOptions() + opts := humanlog.HandlerOptionsFrom(*cfg) log.Print("reading stdin...") if err := humanlog.Scanner(os.Stdin, colorable.NewColorableStdout(), opts); err != nil { @@ -233,3 +234,7 @@ func newApp() *cli.App { } return app } + +func ptr[T any](v T) *T { + return &v +} diff --git a/color.go b/color.go index 9ab79f8..311bd7b 100644 --- a/color.go +++ b/color.go @@ -1,7 +1,10 @@ package humanlog import ( + "fmt" + "github.com/fatih/color" + "github.com/humanlogio/humanlog/internal/pkg/config" ) var DefaultPalette = Palette{ @@ -39,3 +42,116 @@ type Palette struct { FatalLevelColor *color.Color UnknownLevelColor *color.Color } + +func PaletteFrom(pl config.TextPalette) (*Palette, error) { + var err error + out := &Palette{} + out.KeyColor, err = attributesToColor(pl.KeyColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "key", err) + } + out.ValColor, err = attributesToColor(pl.ValColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "val", err) + } + out.TimeLightBgColor, err = attributesToColor(pl.TimeLightBgColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "time_light_bg", err) + } + out.TimeDarkBgColor, err = attributesToColor(pl.TimeDarkBgColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "time_dark_bg", err) + } + out.MsgLightBgColor, err = attributesToColor(pl.MsgLightBgColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "msg_light_bg", err) + } + out.MsgAbsentLightBgColor, err = attributesToColor(pl.MsgAbsentLightBgColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "msg_absent_light_bg", err) + } + out.MsgDarkBgColor, err = attributesToColor(pl.MsgDarkBgColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "msg_dark_bg", err) + } + out.MsgAbsentDarkBgColor, err = attributesToColor(pl.MsgAbsentDarkBgColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "msg_absent_dark_bg", err) + } + out.DebugLevelColor, err = attributesToColor(pl.DebugLevelColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "debug_level", err) + } + out.InfoLevelColor, err = attributesToColor(pl.InfoLevelColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "info_level", err) + } + out.WarnLevelColor, err = attributesToColor(pl.WarnLevelColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "warn_level", err) + } + out.ErrorLevelColor, err = attributesToColor(pl.ErrorLevelColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "error_level", err) + } + out.PanicLevelColor, err = attributesToColor(pl.PanicLevelColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "panic_level", err) + } + out.FatalLevelColor, err = attributesToColor(pl.FatalLevelColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "fatal_level", err) + } + out.UnknownLevelColor, err = attributesToColor(pl.UnknownLevelColor) + if err != nil { + return nil, fmt.Errorf("in palette key %q, %v", "unknown_level", err) + } + return out, err +} + +func attributesToColor(names []string) (*color.Color, error) { + attrs := make([]color.Attribute, 0, len(names)) + for _, name := range names { + attr, ok := colorAttributeIndex[name] + if !ok { + return nil, fmt.Errorf("color %q isn't supported", name) + } + attrs = append(attrs, attr) + } + return color.New(attrs...), nil +} + +var colorAttributeIndex = map[string]color.Attribute{ + "fg_black": color.FgBlack, + "fg_red": color.FgRed, + "fg_green": color.FgGreen, + "fg_yellow": color.FgYellow, + "fg_blue": color.FgBlue, + "fg_magenta": color.FgMagenta, + "fg_cyan": color.FgCyan, + "fg_white": color.FgWhite, + "fg_hi_black": color.FgHiBlack, + "fg_hi_red": color.FgHiRed, + "fg_hi_green": color.FgHiGreen, + "fg_hi_yellow": color.FgHiYellow, + "fg_hi_blue": color.FgHiBlue, + "fg_hi_magenta": color.FgHiMagenta, + "fg_hi_cyan": color.FgHiCyan, + "fg_hi_white": color.FgHiWhite, + "bg_black": color.BgBlack, + "bg_red": color.BgRed, + "bg_green": color.BgGreen, + "bg_yellow": color.BgYellow, + "bg_blue": color.BgBlue, + "bg_magenta": color.BgMagenta, + "bg_cyan": color.BgCyan, + "bg_white": color.BgWhite, + "bg_hi_black": color.BgHiBlack, + "bg_hi_red": color.BgHiRed, + "bg_hi_green": color.BgHiGreen, + "bg_hi_yellow": color.BgHiYellow, + "bg_hi_blue": color.BgHiBlue, + "bg_hi_magenta": color.BgHiMagenta, + "bg_hi_cyan": color.BgHiCyan, + "bg_hi_white": color.BgHiWhite, +} diff --git a/e2e_test.go b/e2e_test.go index 8850a80..672baaf 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -7,6 +7,8 @@ import ( "os" "path/filepath" "testing" + + "github.com/humanlogio/humanlog/internal/pkg/config" ) func TestHarness(t *testing.T) { @@ -30,14 +32,15 @@ func TestHarness(t *testing.T) { if err != nil { t.Fatalf("reading expected output: %v", err) } - optsjson, err := ioutil.ReadFile(filepath.Join(root, de.Name(), "opts.json")) + cfgjson, err := ioutil.ReadFile(filepath.Join(root, de.Name(), "config.json")) if err != nil { - t.Fatalf("reading options: %v", err) + t.Fatalf("reading config: %v", err) } - opts := new(HandlerOptions) - if err := json.Unmarshal(optsjson, &opts); err != nil { - t.Fatalf("unmarshaling options: %v", err) + var cfg config.Config + if err := json.Unmarshal(cfgjson, &cfg); err != nil { + t.Fatalf("unmarshaling config: %v", err) } + opts := HandlerOptionsFrom(cfg) gotw := bytes.NewBuffer(nil) err = Scanner(bytes.NewReader(input), gotw, opts) if err != nil { diff --git a/go.mod b/go.mod index 6ccfc89..0b93110 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/aybabtme/humanlog +module github.com/humanlogio/humanlog go 1.19 diff --git a/handler.go b/handler.go index 052c41c..d3adfdf 100644 --- a/handler.go +++ b/handler.go @@ -1,8 +1,10 @@ package humanlog import ( + "log" "time" + "github.com/humanlogio/humanlog/internal/pkg/config" "github.com/kr/logfmt" ) @@ -45,6 +47,54 @@ type HandlerOptions struct { Palette Palette } +var _ = HandlerOptionsFrom(config.DefaultConfig) // ensure it's valid + +func HandlerOptionsFrom(cfg config.Config) *HandlerOptions { + opts := DefaultOptions + if cfg.Skip != nil { + opts.Skip = sliceToSet(cfg.Skip) + } + if cfg.Keep != nil { + opts.Keep = sliceToSet(cfg.Keep) + } + if cfg.TimeFields != nil { + opts.TimeFields = *cfg.TimeFields + } + if cfg.MessageFields != nil { + opts.MessageFields = *cfg.MessageFields + } + if cfg.LevelFields != nil { + opts.LevelFields = *cfg.LevelFields + } + if cfg.SortLongest != nil { + opts.SortLongest = *cfg.SortLongest + } + if cfg.SkipUnchanged != nil { + opts.SkipUnchanged = *cfg.SkipUnchanged + } + if cfg.Truncates != nil { + opts.Truncates = *cfg.Truncates + } + if cfg.LightBg != nil { + opts.LightBg = *cfg.LightBg + } + if cfg.TruncateLength != nil { + opts.TruncateLength = *cfg.TruncateLength + } + if cfg.TimeFormat != nil { + opts.TimeFormat = *cfg.TimeFormat + } + if cfg.Palette != nil { + pl, err := PaletteFrom(*cfg.Palette) + if err != nil { + log.Printf("invalid palette, using default one: %v", err) + } else { + opts.Palette = *pl + } + } + return opts +} + func (h *HandlerOptions) shouldShowKey(key string) bool { if len(h.Keep) != 0 { if _, keep := h.Keep[key]; keep { @@ -67,3 +117,14 @@ func (h *HandlerOptions) shouldShowUnchanged(key string) bool { } return false } + +func sliceToSet(arr *[]string) map[string]struct{} { + if arr == nil { + return nil + } + out := make(map[string]struct{}) + for _, key := range *arr { + out[key] = struct{}{} + } + return out +} diff --git a/cmd/humanlog/config.go b/internal/pkg/config/config.go similarity index 51% rename from cmd/humanlog/config.go rename to internal/pkg/config/config.go index f41ee37..2ce5034 100644 --- a/cmd/humanlog/config.go +++ b/internal/pkg/config/config.go @@ -1,21 +1,17 @@ -package main +package config import ( "encoding/json" "errors" "fmt" "io/ioutil" - "log" "os" "path/filepath" "strings" "time" - - "github.com/aybabtme/humanlog" - "github.com/fatih/color" ) -var defaultConfig = &Config{ +var DefaultConfig = Config{ Version: 1, Skip: ptr([]string{}), Keep: ptr([]string{}), @@ -33,9 +29,7 @@ var defaultConfig = &Config{ Palette: nil, } -var _ = defaultConfig.toHandlerOptions() // ensure it's valid - -func getDefaultConfigFilepath() (string, error) { +func GetDefaultConfigFilepath() (string, error) { home, err := os.UserHomeDir() if err != nil { return "", fmt.Errorf("$HOME not set, can't determine a config file path") @@ -65,7 +59,7 @@ func getDefaultConfigFilepath() (string, error) { return configFilepath, nil } -func readConfigFile(path string, dflt *Config) (*Config, error) { +func ReadConfigFile(path string, dflt *Config) (*Config, error) { configFile, err := os.Open(path) if err != nil { if !errors.Is(err, os.ErrNotExist) { @@ -151,63 +145,6 @@ func (cfg Config) populateEmpty(other *Config) *Config { return &out } -func (cfg Config) toHandlerOptions() *humanlog.HandlerOptions { - opts := humanlog.DefaultOptions - if cfg.Skip != nil { - opts.Skip = sliceToSet(cfg.Skip) - } - if cfg.Keep != nil { - opts.Keep = sliceToSet(cfg.Keep) - } - if cfg.TimeFields != nil { - opts.TimeFields = *cfg.TimeFields - } - if cfg.MessageFields != nil { - opts.MessageFields = *cfg.MessageFields - } - if cfg.LevelFields != nil { - opts.LevelFields = *cfg.LevelFields - } - if cfg.SortLongest != nil { - opts.SortLongest = *cfg.SortLongest - } - if cfg.SkipUnchanged != nil { - opts.SkipUnchanged = *cfg.SkipUnchanged - } - if cfg.Truncates != nil { - opts.Truncates = *cfg.Truncates - } - if cfg.LightBg != nil { - opts.LightBg = *cfg.LightBg - } - if cfg.TruncateLength != nil { - opts.TruncateLength = *cfg.TruncateLength - } - if cfg.TimeFormat != nil { - opts.TimeFormat = *cfg.TimeFormat - } - if cfg.Palette != nil { - pl, err := cfg.Palette.compile() - if err != nil { - log.Printf("invalid palette, using default one: %v", err) - } else { - opts.Palette = *pl - } - } - return opts -} - -func sliceToSet(arr *[]string) map[string]struct{} { - if arr == nil { - return nil - } - out := make(map[string]struct{}) - for _, key := range *arr { - out[key] = struct{}{} - } - return out -} - type TextPalette struct { KeyColor []string `json:"key"` ValColor []string `json:"val"` @@ -226,119 +163,6 @@ type TextPalette struct { UnknownLevelColor []string `json:"unknown_level"` } -func (pl TextPalette) compile() (*humanlog.Palette, error) { - var err error - out := &humanlog.Palette{} - out.KeyColor, err = attributesToColor(pl.KeyColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "key", err) - } - out.ValColor, err = attributesToColor(pl.ValColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "val", err) - } - out.TimeLightBgColor, err = attributesToColor(pl.TimeLightBgColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "time_light_bg", err) - } - out.TimeDarkBgColor, err = attributesToColor(pl.TimeDarkBgColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "time_dark_bg", err) - } - out.MsgLightBgColor, err = attributesToColor(pl.MsgLightBgColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "msg_light_bg", err) - } - out.MsgAbsentLightBgColor, err = attributesToColor(pl.MsgAbsentLightBgColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "msg_absent_light_bg", err) - } - out.MsgDarkBgColor, err = attributesToColor(pl.MsgDarkBgColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "msg_dark_bg", err) - } - out.MsgAbsentDarkBgColor, err = attributesToColor(pl.MsgAbsentDarkBgColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "msg_absent_dark_bg", err) - } - out.DebugLevelColor, err = attributesToColor(pl.DebugLevelColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "debug_level", err) - } - out.InfoLevelColor, err = attributesToColor(pl.InfoLevelColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "info_level", err) - } - out.WarnLevelColor, err = attributesToColor(pl.WarnLevelColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "warn_level", err) - } - out.ErrorLevelColor, err = attributesToColor(pl.ErrorLevelColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "error_level", err) - } - out.PanicLevelColor, err = attributesToColor(pl.PanicLevelColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "panic_level", err) - } - out.FatalLevelColor, err = attributesToColor(pl.FatalLevelColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "fatal_level", err) - } - out.UnknownLevelColor, err = attributesToColor(pl.UnknownLevelColor) - if err != nil { - return nil, fmt.Errorf("in palette key %q, %v", "unknown_level", err) - } - return out, err -} - -func attributesToColor(names []string) (*color.Color, error) { - attrs := make([]color.Attribute, 0, len(names)) - for _, name := range names { - attr, ok := colorAttributeIndex[name] - if !ok { - return nil, fmt.Errorf("color %q isn't supported", name) - } - attrs = append(attrs, attr) - } - return color.New(attrs...), nil -} - -var colorAttributeIndex = map[string]color.Attribute{ - "fg_black": color.FgBlack, - "fg_red": color.FgRed, - "fg_green": color.FgGreen, - "fg_yellow": color.FgYellow, - "fg_blue": color.FgBlue, - "fg_magenta": color.FgMagenta, - "fg_cyan": color.FgCyan, - "fg_white": color.FgWhite, - "fg_hi_black": color.FgHiBlack, - "fg_hi_red": color.FgHiRed, - "fg_hi_green": color.FgHiGreen, - "fg_hi_yellow": color.FgHiYellow, - "fg_hi_blue": color.FgHiBlue, - "fg_hi_magenta": color.FgHiMagenta, - "fg_hi_cyan": color.FgHiCyan, - "fg_hi_white": color.FgHiWhite, - "bg_black": color.BgBlack, - "bg_red": color.BgRed, - "bg_green": color.BgGreen, - "bg_yellow": color.BgYellow, - "bg_blue": color.BgBlue, - "bg_magenta": color.BgMagenta, - "bg_cyan": color.BgCyan, - "bg_white": color.BgWhite, - "bg_hi_black": color.BgHiBlack, - "bg_hi_red": color.BgHiRed, - "bg_hi_green": color.BgHiGreen, - "bg_hi_yellow": color.BgHiYellow, - "bg_hi_blue": color.BgHiBlue, - "bg_hi_magenta": color.BgHiMagenta, - "bg_hi_cyan": color.BgHiCyan, - "bg_hi_white": color.BgHiWhite, -} - type ColorMode int const ( diff --git a/json_handler_test.go b/json_handler_test.go index dc4a97d..84c98e6 100644 --- a/json_handler_test.go +++ b/json_handler_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/aybabtme/humanlog" + "github.com/humanlogio/humanlog" ) func TestJSONHandler_UnmarshalJSON_ParsesFields(t *testing.T) { diff --git a/test/cases/00001-json/opts.json b/test/cases/00001-json/config.json similarity index 100% rename from test/cases/00001-json/opts.json rename to test/cases/00001-json/config.json diff --git a/test/cases/00002-logfmt/opts.json b/test/cases/00002-logfmt/config.json similarity index 100% rename from test/cases/00002-logfmt/opts.json rename to test/cases/00002-logfmt/config.json diff --git a/test/cases/00003-zap/opts.json b/test/cases/00003-zap/config.json similarity index 100% rename from test/cases/00003-zap/opts.json rename to test/cases/00003-zap/config.json diff --git a/test/cases/00004-mixed/opts.json b/test/cases/00004-mixed/config.json similarity index 100% rename from test/cases/00004-mixed/opts.json rename to test/cases/00004-mixed/config.json diff --git a/test/cases/10000-behavior-base/opts.json b/test/cases/10000-behavior-base/config.json similarity index 100% rename from test/cases/10000-behavior-base/opts.json rename to test/cases/10000-behavior-base/config.json diff --git a/test/cases/10001-behavior-truncates/opts.json b/test/cases/10001-behavior-truncates/config.json similarity index 100% rename from test/cases/10001-behavior-truncates/opts.json rename to test/cases/10001-behavior-truncates/config.json diff --git a/test/cases/20001-strip-doker-compose/opts.json b/test/cases/20001-strip-doker-compose/config.json similarity index 100% rename from test/cases/20001-strip-doker-compose/opts.json rename to test/cases/20001-strip-doker-compose/config.json diff --git a/test/cases/20001-strip-syslog/opts.json b/test/cases/20001-strip-syslog/config.json similarity index 100% rename from test/cases/20001-strip-syslog/opts.json rename to test/cases/20001-strip-syslog/config.json diff --git a/test/cases/90000-misc-unchanged/opts.json b/test/cases/90000-misc-unchanged/config.json similarity index 100% rename from test/cases/90000-misc-unchanged/opts.json rename to test/cases/90000-misc-unchanged/config.json