Repository: kovetskiy/mark (go)

Issue:
Output is too verbose in CI
**Is your feature request related to a problem? Please describe.**

I have created a CI/CD process to sync documents automatically to confluence and everything works fine except that the tool is logging a lot and finding issues can become a problem.
I am using dry run in CI and the full sync in CD.

A clear and concise description of what the problem is and what the feature provides.

I tried to find a switch/flag in the CLI but was not able to do it.

Ideally, the dry run should allow for less verbose logging or log only if something is problematic.





Pull request:
Log levels support
Introducing log levels to support a more generic way of controlling logs.

- Unifies log level support
- Removes contradicting flags where trace and debug can be set simultaneously
- Default log level is INFO as before

Resolves #505.


Patch:
diff --git a/README.md b/README.md
index f745871..69c1ea5 100644
--- a/README.md
+++ b/README.md
@@ -794,8 +794,7 @@ GLOBAL OPTIONS:
    --minor-edit                                  don't send notifications while updating Confluence page. (default: false) [$MARK_MINOR_EDIT]
    --version-message value                       add a message to the page version, to explain the edit (default: "") [$MARK_VERSION_MESSAGE]
    --color value                                 display logs in color. Possible values: auto, never. (default: "auto") [$MARK_COLOR]
-   --debug                                       enable debug logs. (default: false) [$MARK_DEBUG]
-   --trace                                       enable trace logs. (default: false) [$MARK_TRACE]
+   --log-level value                             set the log level. Possible values: TRACE, DEBUG, INFO, WARNING, ERROR, FATAL. (default: "info") [$MARK_LOG_LEVEL]
    --username value, -u value                    use specified username for updating Confluence page. [$MARK_USERNAME]
    --password value, -p value                    use specified token for updating Confluence page. Specify - as password to read password from stdin, or your Personal access token. Username is not mandatory if personal access token is provided. For more info please see: https://developer.atlassian.com/server/confluence/confluence-server-rest-api/#authentication. [$MARK_PASSWORD]
    --target-url value, -l value                  edit specified Confluence page. If -l is not specified, file should contain metadata (see above). [$MARK_TARGET_URL]
diff --git a/main.go b/main.go
index 10a349b..f955461 100644
--- a/main.go
+++ b/main.go
@@ -105,17 +105,11 @@ var flags = []cli.Flag{
 		Usage:   "display logs in color. Possible values: auto, never.",
 		EnvVars: []string{"MARK_COLOR"},
 	}),
-	altsrc.NewBoolFlag(&cli.BoolFlag{
-		Name:    "debug",
-		Value:   false,
-		Usage:   "enable debug logs.",
-		EnvVars: []string{"MARK_DEBUG"},
-	}),
-	altsrc.NewBoolFlag(&cli.BoolFlag{
-		Name:    "trace",
-		Value:   false,
-		Usage:   "enable trace logs.",
-		EnvVars: []string{"MARK_TRACE"},
+	altsrc.NewStringFlag(&cli.StringFlag{
+		Name:    "log-level",
+		Value:   "info",
+		Usage:   "set the log level. Possible values: TRACE, DEBUG, INFO, WARNING, ERROR, FATAL.",
+		EnvVars: []string{"MARK_LOG_LEVEL"},
 	}),
 	altsrc.NewStringFlag(&cli.StringFlag{
 		Name:    "username",
@@ -230,13 +224,8 @@ func main() {
 }
 
 func RunMark(cCtx *cli.Context) error {
-
-	if cCtx.Bool("debug") {
-		log.SetLevel(lorg.LevelDebug)
-	}
-
-	if cCtx.Bool("trace") {
-		log.SetLevel(lorg.LevelTrace)
+	if err := setLogLevel(cCtx); err != nil {
+		return err
 	}
 
 	if cCtx.String("color") == "never" {
@@ -551,7 +540,6 @@ func processFile(
 }
 
 func updateLabels(api *confluence.API, target *confluence.PageInfo, meta *metadata.Meta) {
-
 	labelInfo, err := api.GetPageLabels(target, "global")
 	if err != nil {
 		log.Fatal(err)
@@ -619,3 +607,26 @@ func configFilePath() string {
 	}
 	return filepath.Join(fp, "mark")
 }
+
+func setLogLevel(cCtx *cli.Context) error {
+	logLevel := cCtx.String("log-level")
+	switch strings.ToUpper(logLevel) {
+	case lorg.LevelTrace.String():
+		log.SetLevel(lorg.LevelTrace)
+	case lorg.LevelDebug.String():
+		log.SetLevel(lorg.LevelDebug)
+	case lorg.LevelInfo.String():
+		log.SetLevel(lorg.LevelInfo)
+	case lorg.LevelWarning.String():
+		log.SetLevel(lorg.LevelWarning)
+	case lorg.LevelError.String():
+		log.SetLevel(lorg.LevelError)
+	case lorg.LevelFatal.String():
+		log.SetLevel(lorg.LevelFatal)
+	default:
+		return fmt.Errorf("unknown log level: %s", logLevel)
+	}
+	log.GetLevel()
+
+	return nil
+}


Test patch:
diff --git a/main_test.go b/main_test.go
new file mode 100644
index 0000000..92ffc4d
--- /dev/null
+++ b/main_test.go
@@ -0,0 +1,45 @@
+package main
+
+import (
+	"flag"
+	"testing"
+
+	"github.com/reconquest/pkg/log"
+	"github.com/stretchr/testify/assert"
+	"github.com/urfave/cli/v2"
+)
+
+func Test_setLogLevel(t *testing.T) {
+	type args struct {
+		lvl string
+	}
+	tests := map[string]struct {
+		args        args
+		want        log.Level
+		expectedErr string
+	}{
+		"invalid": {args: args{lvl: "INVALID"}, want: log.LevelInfo, expectedErr: "unknown log level: INVALID"},
+		"empty":   {args: args{lvl: ""}, want: log.LevelInfo, expectedErr: "unknown log level: "},
+		"info":    {args: args{lvl: log.LevelInfo.String()}, want: log.LevelInfo},
+		"debug":   {args: args{lvl: log.LevelDebug.String()}, want: log.LevelDebug},
+		"trace":   {args: args{lvl: log.LevelTrace.String()}, want: log.LevelTrace},
+		"warning": {args: args{lvl: log.LevelWarning.String()}, want: log.LevelWarning},
+		"error":   {args: args{lvl: log.LevelError.String()}, want: log.LevelError},
+		"fatal":   {args: args{lvl: log.LevelFatal.String()}, want: log.LevelFatal},
+	}
+	for name, tt := range tests {
+		t.Run(name, func(t *testing.T) {
+			set := flag.NewFlagSet("test", flag.ContinueOnError)
+			set.String("log-level", tt.args.lvl, "")
+			cliCtx := cli.NewContext(nil, set, nil)
+
+			err := setLogLevel(cliCtx)
+			if tt.expectedErr != "" {
+				assert.EqualError(t, err, tt.expectedErr)
+			} else {
+				assert.NoError(t, err)
+				assert.Equal(t, tt.want, log.GetLevel())
+			}
+		})
+	}
+}