Repository: datreeio/datree
Pull request #904 (2023-03-15 14:36:16)

fix: error messages logged to stderr instead of stdout
**test case:** 
`./datree test ~/.datree/k8s-demo.yam -o json 1>stdout.log 2> error.log`

![image](https://user-images.githubusercontent.com/29673827/225576574-1202dff1-cdf9-42fa-876e-243b19c32762.png)

**info new line**

![image](https://user-images.githubusercontent.com/29673827/225591710-60725ec8-00a2-4b4a-b7f7-4979d14403f7.png)

**format output:** 

`./datree test ~/.datree/k8s-demo.yaml -o json | jq`

![image](https://user-images.githubusercontent.com/29673827/225591834-0f0fff36-2fa2-4256-ac13-c73574f1befc.png)




Patch:
diff --git a/cmd/kustomize/main.go b/cmd/kustomize/main.go
index a34f1e9..feaf8d2 100644
--- a/cmd/kustomize/main.go
+++ b/cmd/kustomize/main.go
@@ -58,7 +58,7 @@ func New(testCtx *test.TestCommandContext, kustomizeCtx *KustomizeContext) *cobr
 			var err error = nil
 			defer func() {
 				if err != nil {
-					testCtx.Printer.PrintMessage(strings.Join([]string{"\n", err.Error(), "\n"}, ""), "error")
+					testCtx.Printer.PrintError(strings.Join([]string{"\n", err.Error(), "\n"}, ""), "error")
 				}
 			}()
 
diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go
index 634b828..70e0407 100644
--- a/pkg/printer/printer.go
+++ b/pkg/printer/printer.go
@@ -13,7 +13,8 @@ import (
 	"github.com/olekukonko/tablewriter"
 )
 
-var out = color.Output
+var StdOut = color.Output
+var StdErr = color.Error
 
 type Printer struct {
 	Theme *Theme
@@ -318,9 +319,14 @@ func (p *Printer) GetSummaryTableText(summary Summary) string {
 	return sb.String()
 }
 
-func (p *Printer) printInColor(title string, color *color.Color) {
+func (p *Printer) printStdErrInColor(title string, color *color.Color) {
 	colorPrintFn := color.FprintfFunc()
-	colorPrintFn(out, title)
+	colorPrintFn(StdErr, title)
+}
+
+func (p *Printer) printStdOutInColor(title string, color *color.Color) {
+	colorPrintFn := color.FprintfFunc()
+	colorPrintFn(StdOut, title)
 }
 
 func (p *Printer) GetTextInColor(text string, color *color.Color) string {
@@ -345,13 +351,18 @@ func (p *Printer) createNewColor(clr string) *color.Color {
 	}
 }
 
+func (p *Printer) PrintError(messageText string, messageColor string) {
+	colorPrintFn := p.createNewColor(messageColor)
+	p.printStdErrInColor(messageText, colorPrintFn)
+}
+
 func (p *Printer) PrintMessage(messageText string, messageColor string) {
 	colorPrintFn := p.createNewColor(messageColor)
-	p.printInColor(messageText, colorPrintFn)
+	p.printStdOutInColor(messageText, colorPrintFn)
 }
 
 func (p *Printer) PrintPromptMessage(promptMessage string) {
-	fmt.Fprint(out, color.HiCyanString("\n\n"+promptMessage+" (Y/n)\n"))
+	fmt.Fprint(StdOut, color.HiCyanString("\n\n"+promptMessage+" (Y/n)\n"))
 }
 
 func (p *Printer) getPassedYamlValidationText() string {


Test patch:
diff --git a/cmd/kustomize/main_test.go b/cmd/kustomize/main_test.go
index a48b22a..3287df8 100644
--- a/cmd/kustomize/main_test.go
+++ b/cmd/kustomize/main_test.go
@@ -159,6 +159,10 @@ func (p *PrinterMock) GetEvaluationSummaryText(evaluationSummary printer.Evaluat
 	return ""
 }
 
+func (p *PrinterMock) PrintError(messageText string, messageColor string) {
+	p.Called(messageText, messageColor)
+}
+
 func (p *PrinterMock) PrintMessage(messageText string, messageColor string) {
 	p.Called(messageText, messageColor)
 }
diff --git a/cmd/test/main.go b/cmd/test/main.go
index 3ffd2bd..50664da 100644
--- a/cmd/test/main.go
+++ b/cmd/test/main.go
@@ -120,6 +120,7 @@ type EvaluationPrinter interface {
 	GetWarningsText(warnings []printer.Warning, quiet bool) string
 	GetSummaryTableText(summary printer.Summary) string
 	PrintMessage(messageText string, messageColor string)
+	PrintError(messageText string, messageColor string)
 	PrintPromptMessage(promptMessage string)
 	GetEvaluationSummaryText(evaluationSummary printer.EvaluationSummary, k8sVersion string) string
 	SetTheme(theme *printer.Theme)
@@ -223,7 +224,7 @@ func New(ctx *TestCommandContext) *cobra.Command {
 			var err error = nil
 			defer func() {
 				if err != nil {
-					ctx.Printer.PrintMessage(strings.Join([]string{"\n", err.Error(), "\n"}, ""), "error")
+					ctx.Printer.PrintError(strings.Join([]string{"\n", err.Error(), "\n"}, ""), "error")
 				}
 			}()
 
@@ -467,8 +468,8 @@ func test(ctx *TestCommandContext, paths []string, testCommandData *TestCommandD
 		}
 	}
 
-	if testCommandData.IsOffline {
-		ctx.Printer.PrintMessage("[INFO] You're running Datree in offline mode", "cyan")
+	if testCommandData.IsOffline && !evaluation.IsFormattedOutputOption(testCommandData.Output) {
+		ctx.Printer.PrintMessage("\n[INFO] You're running Datree in offline mode", "cyan")
 	}
 
 	if err != nil {
diff --git a/cmd/test/main_test.go b/cmd/test/main_test.go
index 9e7c954..166139d 100644
--- a/cmd/test/main_test.go
+++ b/cmd/test/main_test.go
@@ -159,6 +159,10 @@ func (p *PrinterMock) GetEvaluationSummaryText(evaluationSummary printer.Evaluat
 	return ""
 }
 
+func (p *PrinterMock) PrintError(messageText string, messageColor string) {
+	p.Called(messageText, messageColor)
+}
+
 func (p *PrinterMock) PrintMessage(messageText string, messageColor string) {
 	p.Called(messageText, messageColor)
 }
@@ -249,6 +253,7 @@ func TestTestFlow(t *testing.T) {
 			printerMock.On("GetWarningsText", mock.Anything)
 			printerMock.On("GetSummaryTableText", mock.Anything)
 			printerMock.On("GetEvaluationSummaryText", mock.Anything, mock.Anything)
+			printerMock.On("PrintError", mock.Anything, mock.Anything)
 			printerMock.On("PrintMessage", mock.Anything, mock.Anything)
 			printerMock.On("PrintPromptMessage", mock.Anything)
 			printerMock.On("SetTheme", mock.Anything)
@@ -698,6 +703,7 @@ func setup() {
 	printerMock.On("GetWarningsText", mock.Anything)
 	printerMock.On("GetSummaryTableText", mock.Anything)
 	printerMock.On("GetEvaluationSummaryText", mock.Anything, mock.Anything)
+	printerMock.On("PrintError", mock.Anything, mock.Anything)
 	printerMock.On("PrintMessage", mock.Anything, mock.Anything)
 	printerMock.On("PrintPromptMessage", mock.Anything)
 	printerMock.On("SetTheme", mock.Anything)
diff --git a/pkg/printer/printer_test.go b/pkg/printer/printer_test.go
index 8dc6ca1..5168674 100644
--- a/pkg/printer/printer_test.go
+++ b/pkg/printer/printer_test.go
@@ -74,7 +74,7 @@ func TestGetWarningsText(t *testing.T) {
 
 	t.Run("Test GetWarningsText", func(t *testing.T) {
 
-		out = new(bytes.Buffer)
+		StdOut = new(bytes.Buffer)
 
 		got := printer.GetWarningsText(warnings, false)
 
@@ -148,7 +148,7 @@ SKIPPED
 
 	t.Run("Test GetWarningsText simple output", func(t *testing.T) {
 
-		out = new(bytes.Buffer)
+		StdOut = new(bytes.Buffer)
 
 		printer.SetTheme(CreateSimpleTheme())
 
@@ -215,7 +215,7 @@ https://github.com/datreeio/helm-datree
 
 func TestGetEvaluationSummaryText(t *testing.T) {
 	t.Run("Test GetEvaluationSummaryText", func(t *testing.T) {
-		out = new(bytes.Buffer)
+		StdOut = new(bytes.Buffer)
 		printer := CreateNewPrinter()
 		summary := EvaluationSummary{
 			ConfigsCount:              6,
@@ -243,7 +243,7 @@ func TestGetEvaluationSummaryText(t *testing.T) {
 	})
 
 	t.Run("Test GetEvaluationSummaryText with no connection warning", func(t *testing.T) {
-		out = new(bytes.Buffer)
+		StdOut = new(bytes.Buffer)
 		printer := CreateNewPrinter()
 		summary := EvaluationSummary{
 			ConfigsCount:              6,