Skip to content

Commit

Permalink
cmd: add --log-level CLI flag (#15)
Browse files Browse the repository at this point in the history
Allow specifying the log level directly as the global CLI flag
`--log-level`.

Signed-off-by: Luiz Aoqui <[email protected]>
  • Loading branch information
lgfa29 authored Oct 17, 2024
1 parent 2de64b4 commit 338db99
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 34 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Tests

on: [pull_request]

jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.22.5

- name: Test
run: go test -v ./...
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ require (
github.com/briandowns/spinner v1.23.1
github.com/fatih/color v1.17.0
github.com/jedib0t/go-pretty/v6 v6.6.0
github.com/loopholelabs/logging v0.3.0
github.com/loopholelabs/logging v0.3.1
github.com/mattn/go-isatty v0.0.20
github.com/mitchellh/mapstructure v1.5.0
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand All @@ -28,6 +30,7 @@ require (
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/loopholelabs/logging v0.3.0 h1:Rfo9fGdBk4nwbNdiLrVUF7JkYoC67dvGDodkRe81xF0=
github.com/loopholelabs/logging v0.3.0/go.mod h1:uRDUydiqPqKbZkb0WoQ3dfyAcJ2iOMhxdEafZssLVv0=
github.com/loopholelabs/logging v0.3.1 h1:VA9DF3WrbmvJC1uQJ/XcWgz8KWXydWwe3BdDiMbN2FY=
github.com/loopholelabs/logging v0.3.1/go.mod h1:uRDUydiqPqKbZkb0WoQ3dfyAcJ2iOMhxdEafZssLVv0=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
Expand Down
74 changes: 43 additions & 31 deletions pkg/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ type Command[T config.Config] struct {
newConfig config.New[T]
config T
setupCommands []SetupCommand[T]

format printer.Format
debug bool
logLevel types.Level

// The following io.Writer values should be used when outputting text. They
// default to os.Stdout and os.Stderr but may be changed during tests.
stdout io.Writer
stderr io.Writer
}

var (
Expand Down Expand Up @@ -72,33 +81,32 @@ func New[T config.Config](cli string, short string, long string, noargs bool, ve
version: version,
newConfig: newConfig,
setupCommands: setupCommands,
stdout: os.Stdout,
stderr: os.Stderr,
}
}

func (c *Command[T]) Execute(ctx context.Context, commandType Type) int {
var format printer.Format
var debug bool

devEnv := fmt.Sprintf("%s_DISABLE_DEV_WARNING", strings.ToUpper(replacer.Replace(c.cli)))
devWarning := fmt.Sprintf("!! WARNING: You are using a self-compiled binary which is not officially supported.\n!! To dismiss this warning, set %s=true\n\n", devEnv)

if _, ok := os.LookupEnv(devEnv); !ok {
if c.version.GitCommit() == "" || c.version.GoVersion() == "" || c.version.BuildDate() == "" || c.version.Version() == "" || c.version.Platform() == "" {
_, _ = fmt.Fprintf(os.Stderr, devWarning)
_, _ = fmt.Fprintf(c.stderr, devWarning)
}
}

err := c.runCmd(ctx, &format, &debug, commandType)
err := c.runCmd(ctx, commandType)
if err == nil {
return 0
}

// print any user specific messages first
switch format {
switch c.format {
case printer.JSON:
_, _ = fmt.Fprintf(os.Stderr, `{"error": "%s"}`, err)
_, _ = fmt.Fprintf(c.stderr, `{"error": "%s"}`, err)
default:
_, _ = fmt.Fprintf(os.Stderr, "Error: %s\n", err)
_, _ = fmt.Fprintf(c.stderr, "Error: %s\n", err)
}

logClosersLock.Lock()
Expand All @@ -118,7 +126,7 @@ func (c *Command[T]) Execute(ctx context.Context, commandType Type) int {

// runCmd adds all child commands to the root command, sets flags
// appropriately, and runs the root command.
func (c *Command[T]) runCmd(ctx context.Context, format *printer.Format, debug *bool, commandType Type) error {
func (c *Command[T]) runCmd(ctx context.Context, commandType Type) error {
c.config = c.newConfig()

configDir, err := c.config.DefaultConfigDir()
Expand Down Expand Up @@ -147,41 +155,41 @@ func (c *Command[T]) runCmd(ctx context.Context, format *printer.Format, debug *
cobra.OnInitialize(func() {
err := c.initConfig()
if err != nil {
switch *format {
switch c.format {
case printer.JSON:
_, _ = fmt.Fprintf(os.Stderr, `{"error": "%s"}`, err)
_, _ = fmt.Fprintf(c.stderr, `{"error": "%s"}`, err)
default:
_, _ = fmt.Fprintf(os.Stderr, "Error: %s\n", err)
_, _ = fmt.Fprintf(c.stderr, "Error: %s\n", err)
}

os.Exit(cmdutils.FatalErrExitCode)
}

ch.SetDebug(debug)
ch.SetDebug(&c.debug)

ch.Printer = printer.NewPrinter(format)
ch.Printer = printer.NewPrinter(&c.format)

if strings.TrimSpace(logFile) == "" {
logOutput = os.Stderr
logOutput = c.stderr
} else {
if err := os.MkdirAll(filepath.Dir(logFile), 0700); err != nil {
switch *format {
switch c.format {
case printer.JSON:
_, _ = fmt.Fprintf(os.Stderr, `{"error": "%s"}`, err)
_, _ = fmt.Fprintf(c.stderr, `{"error": "%s"}`, err)
default:
_, _ = fmt.Fprintf(os.Stderr, "Error: %s\n", err)
_, _ = fmt.Fprintf(c.stderr, "Error: %s\n", err)
}

os.Exit(cmdutils.FatalErrExitCode)
}

fileLogOutput, err := os.OpenFile(logFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0700)
if err != nil {
switch *format {
switch c.format {
case printer.JSON:
_, _ = fmt.Fprintf(os.Stderr, `{"error": "%s"}`, err)
_, _ = fmt.Fprintf(c.stderr, `{"error": "%s"}`, err)
default:
_, _ = fmt.Fprintf(os.Stderr, "Error: %s\n", err)
_, _ = fmt.Fprintf(c.stderr, "Error: %s\n", err)
}

os.Exit(cmdutils.FatalErrExitCode)
Expand All @@ -192,24 +200,19 @@ func (c *Command[T]) runCmd(ctx context.Context, format *printer.Format, debug *
logClosers = append(logClosers, fileLogOutput.Close)

if ch.Debug() {
logOutput = io.MultiWriter(fileLogOutput, os.Stderr)
logOutput = io.MultiWriter(fileLogOutput, c.stderr)
} else {
logOutput = fileLogOutput
}
}

switch *format {
switch c.format {
case printer.JSON:
ch.Logger = logging.New(logging.Zerolog, strings.ToLower(c.cli), logOutput)
default:
ch.Logger = logging.New(logging.Slog, strings.ToLower(c.cli), logOutput)
}

if ch.Debug() {
ch.Logger.SetLevel(types.TraceLevel)
} else {
ch.Logger.SetLevel(types.InfoLevel)
}
ch.Logger.SetLevel(c.logLevel)
})

c.command.SilenceUsage = true
Expand All @@ -222,19 +225,28 @@ func (c *Command[T]) runCmd(ctx context.Context, format *printer.Format, debug *

c.config.RootPersistentFlags(c.command.PersistentFlags())

c.command.PersistentFlags().VarP(printer.NewFormatValue(printer.Human, format), "format", "f", "Show output in a specific format. Possible values: [human, json]")
c.command.PersistentFlags().VarP(printer.NewFormatValue(printer.Human, &c.format), "format", "f", "Show output in a specific format. Possible values: [human, json]")
if err = viper.BindPFlag("format", c.command.PersistentFlags().Lookup("format")); err != nil {
return err
}
_ = c.command.RegisterFlagCompletionFunc("format", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"human", "json"}, cobra.ShellCompDirectiveDefault
})

c.command.PersistentFlags().BoolVar(debug, "debug", false, "Enable debug mode")
c.command.PersistentFlags().BoolVar(&c.debug, "debug", false, "Enable debug mode")
if err = viper.BindPFlag("debug", c.command.PersistentFlags().Lookup("debug")); err != nil {
return err
}

c.logLevel = types.InfoLevel
c.command.PersistentFlags().VarP(&c.logLevel, "log-level", "", "")
if err = viper.BindPFlag("log-level", c.command.PersistentFlags().Lookup("debug")); err != nil {
return err
}
_ = c.command.RegisterFlagCompletionFunc("log-level", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"fatal", "error", "warn", "info", "debug", "trace"}, cobra.ShellCompDirectiveDefault
})

c.command.PersistentFlags().BoolVar(&color.NoColor, "no-color", false, "Disable color output")
if err = viper.BindPFlag("no-color", c.command.PersistentFlags().Lookup("no-color")); err != nil {
return err
Expand Down
Loading

0 comments on commit 338db99

Please sign in to comment.