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 description:
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())
+			}
+		})
+	}
+}