Repository: apache/camel-k
Pull request #2245 (2021-04-29 08:11:33)

Improve readability of error messages in Camel K
<!-- Description -->
Colorize pod log messages based on their log level to improve
readability of errors, warnings and pod output messages



<!--
Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". 

You can (optionally) mark this PR with labels "kind/bug" or "kind/feature" to make sure
the text is added to the right section of the release notes. 
-->

**Release Note**
```release-note
Colorize pod log messages based on their log level
```

orpiske: Samples for the logs with colorization: 

With errors on the pods/build:
![colorize-2-with-errors](https://user-images.githubusercontent.com/1851560/116520736-6621f780-a8d3-11eb-80d3-e82885322510.png)

With a pod output (ie.: when the pod uses `System.out.println`):
![colorize-2-with-sout](https://user-images.githubusercontent.com/1851560/116520756-6ae6ab80-a8d3-11eb-96ac-02698848e118.png)

With a basic pod output (ie.: using `to("log:info")`):
![colorize-2-working](https://user-images.githubusercontent.com/1851560/116520768-7043f600-a8d3-11eb-92d7-72ced3493d40.png)

orpiske: The idea is to propose a way to improve spotting build errors (as suggested on #185) 
astefanutti: Nice. I wonder, does it support https://no-color.org?
orpiske: > Nice. I wonder, does it support https://no-color.org?

Thanks. No, but I can include that. 
orpiske: > Nice. I wonder, does it support https://no-color.org?

Done. I also added a small note on the readme for that. 
astefanutti: Out of curiosity, I wonder if we could enable colors in the integration logs directly. IIRC, it used to be enabled with Spring Boot, when we supported it as runtime. Is this possible with Quarkus? If yes, that would benefit globally, and avoid client parsing altogether.
orpiske: > Out of curiosity, I wonder if we could enable colors in the integration logs directly. IIRC, it used to be enabled with Spring Boot, when we supported it as runtime. Is this possible with Quarkus? If yes, that would benefit globally, and avoid client parsing altogether.

IIRC, yes. I can take a look at this route, as well. I am not sure how it would work w/ displaying the logs via kubectl though. In this case it is done on the client side, so if the user used kubectl / oc to read the logs, there is no impact. 
orpiske: > Out of curiosity, I wonder if we could enable colors in the integration logs directly. IIRC, it used to be enabled with Spring Boot, when we supported it as runtime. Is this possible with Quarkus? If yes, that would benefit globally, and avoid client parsing altogether.

This works: `kamel run --property quarkus.log.console.color=true Basic.java`. So, that's an even easier alternative. Maybe we could make this property enabled by default instead of doing it client side? wdyt?
orpiske: ![logging-via-quarkus](https://user-images.githubusercontent.com/1851560/116537621-c1111a00-a8e6-11eb-9d0b-ad1ca0d2d1ea.png)

astefanutti: Very nice! Yes, it'd +1 to enabled this by default. Maybe we can add a `noColor` option to the Quarkus trait to have quick way to disable it.

Have you tested the behaviour of `kubectl logs`?
orpiske: > Very nice! Yes, it'd +1 to enabled this by default. Maybe we can add a `noColor` option to the Quarkus trait to have quick way to disable it.

Awesome! I'll take a look at that. It seems much better than the original approach.

> 
> Have you tested the behaviour of `kubectl logs`?

Yes I did. It works just as nice via `kubectl`. Also, there is no visual harm when viewing them via the UI:

![Screenshot_2021-04-29 Kubernetes Dashboard](https://user-images.githubusercontent.com/1851560/116538203-8196fd80-a8e7-11eb-8d34-246ed7aaced6.png)

I think it should be reasonably safe. 

One last benefit is that doing it via the quarkus property also enables it on the `kamel local run` (which was not supported originally).


lburgazzoli: maybe we can set this logging option via an env var when the integration is deployed, so the user has the option to override it using properties (or env var)
astefanutti: Awesome! Maybe it is a good opportunity to introduce a logging trait: #580?
orpiske: > Awesome! Maybe it is a good opportunity to introduce a logging trait: #580?

I'm not sure if my golang skills are up to the task, but I can give it a try too :) 
orpiske: @astefanutti @lburgazzoli Here we go. This adds a new trait `logging` to control turning on/off the color. It's very rudimentary for now, but I believe we could extend it/adjust it for #580. 

Currently the logging is adjusted by injecting an environment variable `QUARKUS_LOG_CONSOLE_COLOR` with the appropriate value as set via `--trait logging.color=(true/false)` .

Patch:
diff --git a/deploy/traits.yaml b/deploy/traits.yaml
index 6f90900a7..378601f21 100755
--- a/deploy/traits.yaml
+++ b/deploy/traits.yaml
@@ -301,7 +301,7 @@ traits:
     description: Can be used to enable or disable a trait. All traits share this common
       property.
   - name: discovery-cache
-    type: ./pkg/trait.discoveryCacheType
+    type: camel-k/pkg/trait.discoveryCacheType
     description: Discovery client cache to be used, either `disabled`, `disk` or `memory`
       (default `memory`)
 - name: ingress
@@ -561,6 +561,21 @@ traits:
   - name: auto
     type: bool
     description: Enable automatic discovery of all trait properties.
+- name: logging
+  platform: false
+  profiles:
+  - Kubernetes
+  - Knative
+  - OpenShift
+  description: This trait is used to control logging options (such as color)
+  properties:
+  - name: enabled
+    type: bool
+    description: Can be used to enable or disable a trait. All traits share this common
+      property.
+  - name: color
+    type: bool
+    description: Colorize the log output
 - name: master
   platform: false
   profiles:
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index e3d322eb2..d1f232e3b 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -50,6 +50,7 @@
 ** xref:traits:kamelets.adoc[Kamelets]
 ** xref:traits:knative-service.adoc[Knative Service]
 ** xref:traits:knative.adoc[Knative]
+** xref:traits:logging.adoc[Logging]
 ** xref:traits:master.adoc[Master]
 ** xref:traits:openapi.adoc[Openapi]
 ** xref:traits:owner.adoc[Owner]
diff --git a/docs/modules/traits/pages/gc.adoc b/docs/modules/traits/pages/gc.adoc
index 375057d57..c1a693780 100755
--- a/docs/modules/traits/pages/gc.adoc
+++ b/docs/modules/traits/pages/gc.adoc
@@ -26,7 +26,7 @@ The following configuration options are available:
 | Can be used to enable or disable a trait. All traits share this common property.
 
 | gc.discovery-cache
-| ./pkg/trait.discoveryCacheType
+| camel-k/pkg/trait.discoveryCacheType
 | Discovery client cache to be used, either `disabled`, `disk` or `memory` (default `memory`)
 
 |===
diff --git a/docs/modules/traits/pages/logging.adoc b/docs/modules/traits/pages/logging.adoc
new file mode 100755
index 000000000..d2f979bdf
--- /dev/null
+++ b/docs/modules/traits/pages/logging.adoc
@@ -0,0 +1,34 @@
+= Logging Trait
+
+// Start of autogenerated code - DO NOT EDIT! (description)
+This trait is used to control logging options (such as color)
+
+
+This trait is available in the following profiles: **Kubernetes, Knative, OpenShift**.
+
+// End of autogenerated code - DO NOT EDIT! (description)
+// Start of autogenerated code - DO NOT EDIT! (configuration)
+== Configuration
+
+Trait properties can be specified when running any integration with the CLI:
+[source,console]
+----
+$ kamel run --trait logging.[key]=[value] --trait logging.[key2]=[value2] integration.groovy
+----
+The following configuration options are available:
+
+[cols="2m,1m,5a"]
+|===
+|Property | Type | Description
+
+| logging.enabled
+| bool
+| Can be used to enable or disable a trait. All traits share this common property.
+
+| logging.color
+| bool
+| Colorize the log output
+
+|===
+
+// End of autogenerated code - DO NOT EDIT! (configuration)
diff --git a/pkg/trait/logging.go b/pkg/trait/logging.go
new file mode 100644
index 000000000..7c1776a27
--- /dev/null
+++ b/pkg/trait/logging.go
@@ -0,0 +1,59 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package trait
+
+import (
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/util"
+	"github.com/apache/camel-k/pkg/util/envvar"
+	"strconv"
+)
+
+const (
+	envVarQuarkusLogConsoleColor = "QUARKUS_LOG_CONSOLE_COLOR"
+)
+
+// This trait is used to control logging options (such as color)
+//
+// +camel-k:trait=logging
+type loggingTrait struct {
+	BaseTrait `property:",squash"`
+	// Colorize the log output
+	Color *bool `property:"color" json:"color,omitempty"`
+}
+
+func newLoggingTraitTrait() Trait {
+	return &loggingTrait{
+		BaseTrait: NewBaseTrait("logging", 800),
+		Color:     util.BoolP(true),
+	}
+}
+
+func (l loggingTrait) Configure(environment *Environment) (bool, error) {
+	if l.Enabled != nil && !*l.Enabled {
+		return false, nil
+	}
+
+	return environment.IntegrationInPhase(v1.IntegrationPhaseDeploying, v1.IntegrationPhaseRunning), nil
+}
+
+func (l loggingTrait) Apply(environment *Environment) error {
+	envvar.SetVal(&environment.EnvVars, envVarQuarkusLogConsoleColor, strconv.FormatBool(*l.Color))
+
+	return nil
+}
diff --git a/pkg/trait/trait_register.go b/pkg/trait/trait_register.go
index 30e89bd08..f8e079459 100644
--- a/pkg/trait/trait_register.go
+++ b/pkg/trait/trait_register.go
@@ -50,4 +50,5 @@ func init() {
 	AddToTraits(newServiceBindingTrait)
 	AddToTraits(newOwnerTrait)
 	AddToTraits(newPdbTrait)
+	AddToTraits(newLoggingTraitTrait)
 }


Test patch:
diff --git a/pkg/trait/logging_test.go b/pkg/trait/logging_test.go
new file mode 100644
index 000000000..020d65ada
--- /dev/null
+++ b/pkg/trait/logging_test.go
@@ -0,0 +1,96 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package trait
+
+import (
+	"context"
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/util/camel"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/stretchr/testify/assert"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"testing"
+)
+
+func createLoggingTestEnv(t *testing.T) *Environment {
+	c, err := camel.DefaultCatalog()
+	if err != nil {
+		panic(err)
+	}
+
+	res := &Environment{
+		C:            context.TODO(),
+		CamelCatalog: c,
+		Catalog:      NewCatalog(context.TODO(), nil),
+		Integration: &v1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test",
+				Namespace: "ns",
+			},
+			Status: v1.IntegrationStatus{
+				Phase: v1.IntegrationPhaseDeploying,
+			},
+			Spec: v1.IntegrationSpec{
+				Profile: v1.TraitProfileOpenShift,
+			},
+		},
+		IntegrationKit: &v1.IntegrationKit{
+			Status: v1.IntegrationKitStatus{
+				Phase: v1.IntegrationKitPhaseBuildSubmitted,
+			},
+		},
+		Platform: &v1.IntegrationPlatform{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "ns",
+			},
+			Spec: v1.IntegrationPlatformSpec{
+				Cluster: v1.IntegrationPlatformClusterOpenShift,
+			},
+		},
+		EnvVars:        make([]corev1.EnvVar, 0),
+		ExecutedTraits: make([]Trait, 0),
+		Resources:      kubernetes.NewCollection(),
+	}
+
+	return res
+}
+
+func NewLoggingTestCatalog() *Catalog {
+	return NewCatalog(context.TODO(), nil)
+}
+
+func TestEmptyLoggingTrait(t *testing.T) {
+	env := createLoggingTestEnv(t)
+	err := NewLoggingTestCatalog().apply(env)
+
+	assert.Nil(t, err)
+	assert.NotEmpty(t, env.ExecutedTraits)
+
+	quarkusConsoleColor := false
+
+	for _, e := range env.EnvVars {
+		if e.Name == envVarQuarkusLogConsoleColor {
+			if e.Value == "true" {
+				quarkusConsoleColor = true
+			}
+		}
+	}
+
+	assert.True(t, quarkusConsoleColor)
+}