You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2022/05/03 12:51:22 UTC

[camel-k] 04/05: (#3053): Adds ability to modify the level of the operator logs

This is an automated email from the ASF dual-hosted git repository.

pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit f7fdf356a1567e831490f09df2102684c5cd3339
Author: phantomjinx <p....@phantomjinx.co.uk>
AuthorDate: Tue Apr 19 13:58:09 2022 +0100

    (#3053): Adds ability to modify the level of the operator logs
    
    * Exposes a switch --log-level (-v) at the CLI level allowing the operator
      to have its level of logging changed (info, debug, 0 ... 10)
    
    * The switch translates to an env variable LOG_LEVEL which is included in
      the operator deployment. It is this env var that determines the internal
      level of the log.
    
    * install.go
     * The CLI switch for setting the log-level
    
    * operator.go
     * The setting of the log-level based on the env variable
    
    * Adds debug logging for building the integration kits and integrations
    
    * Wraps errors when building integration kits to identify more precisely
      where the error is occurring and the reason
---
 config/manager/patch-log-level.yaml                | 22 ++++++++
 e2e/common/cli/install_test.go                     | 15 ++++++
 install/Makefile                                   | 22 +++++++-
 pkg/cmd/install.go                                 |  7 +++
 pkg/cmd/install_test.go                            | 63 ++++++++++++++++++++++
 pkg/cmd/operator/operator.go                       | 42 ++++++++++++++-
 pkg/controller/integration/build_kit.go            | 29 +++++++---
 .../integration/integration_controller.go          |  2 +
 pkg/controller/integration/kits.go                 | 13 +++++
 pkg/resources/resources.go                         | 44 +++++++++++++++
 pkg/trait/trait.go                                 | 19 ++++++-
 11 files changed, 268 insertions(+), 10 deletions(-)

diff --git a/config/manager/patch-log-level.yaml b/config/manager/patch-log-level.yaml
new file mode 100644
index 000000000..27a9ec293
--- /dev/null
+++ b/config/manager/patch-log-level.yaml
@@ -0,0 +1,22 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+- op: add
+  path: /spec/template/spec/containers/0/env/-
+  value:
+    name: LOG_LEVEL
+    value: "info"
diff --git a/e2e/common/cli/install_test.go b/e2e/common/cli/install_test.go
index b52f6c772..d1a76dc3c 100644
--- a/e2e/common/cli/install_test.go
+++ b/e2e/common/cli/install_test.go
@@ -39,6 +39,7 @@ import (
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/util/openshift"
 	console "github.com/openshift/api/console/v1"
+	corev1 "k8s.io/api/core/v1"
 )
 
 func TestBasicInstallation(t *testing.T) {
@@ -148,3 +149,17 @@ func TestInstallSkipDefaultKameletsInstallation(t *testing.T) {
 		Expect(KameletList(ns)()).Should(BeEmpty())
 	})
 }
+
+func TestInstallDebugLogging(t *testing.T) {
+	WithNewTestNamespace(t, func(ns string) {
+		Expect(Kamel("install", "-n", ns, "-z", "debug").Execute()).To(Succeed())
+
+		podFunc := OperatorPod(ns)
+		Eventually(podFunc).ShouldNot(BeNil())
+
+		pod := podFunc()
+		logs := Logs(ns, pod.Name, corev1.PodLogOptions{})
+		Eventually(logs).ShouldNot(BeEmpty())
+		Eventually(logs).Should(ContainSubstring("DEBUG level messages will be logged"))
+	})
+}
diff --git a/install/Makefile b/install/Makefile
index faf8466d6..f9aa2708f 100644
--- a/install/Makefile
+++ b/install/Makefile
@@ -48,6 +48,8 @@ MONITORING ?= false
 MONITORING_PORT ?= 8080
 # Health Port: integer
 HEALTH_PORT ?= 8081
+# Operator Logging Level: string [info, debug, 0, 1]
+LOGGING_LEVEL ?= info
 
 CONFIG := ../config
 MANAGER := $(CONFIG)/manager
@@ -67,6 +69,7 @@ ROLE_TO_CROLE_PATCH := $(RBAC)/patch-role-to-clusterrole
 ROLEBIN_TO_CROLEBIN_PATCH := $(RBAC)/patch-rolebinding-to-clusterrolebinding
 # Operator patches
 PORTS_PATCH := patch-ports
+LOG_LEVEL_PATCH := patch-log-level
 IMAGE_PULL_POLICY_PATCH := patch-image-pull-policy-always
 WATCH_NAMESPACE_PATCH := patch-watch-namespace-global
 # Platform patches
@@ -229,7 +232,7 @@ else
 		sed 's/$(PLACEHOLDER)/$(NAMESPACE)/'
 endif
 
-.PHONY: operator .operator-port-patch .operator-can-monitor
+.PHONY: operator .operator-port-patch .operator-can-monitor .operator-log-level-patch
 
 #
 # Customizes the port patch
@@ -240,6 +243,12 @@ endif
 	@sed -i 's/--health-port=.*/--health-port=$(HEALTH_PORT)/' $(MANAGER)/$(PORTS_PATCH).$(YAML)
 	@sed -i '/path:.*\/httpGet\/port/,/- op/{s/value: .*/value: $(HEALTH_PORT)/}' $(MANAGER)/$(PORTS_PATCH).$(YAML)
 
+#
+# Customizes the log level patch
+#
+.operator-log-level-patch:
+	@sed -i 's/    value:.*/    value: "$(LOGGING_LEVEL)"/' $(MANAGER)/$(LOG_LEVEL_PATCH).$(YAML)
+
 .operator-can-monitor: kubectl
 	@output=$$(kubectl get crd prometheusrules.monitoring.coreos.com 2>&1) || (echo "****" && echo "**** ERROR: Montoring not available as Prometheus CRDs not installed in cluster ****" && echo "****"; exit 1)
 
@@ -258,9 +267,10 @@ endif
 #   MONITORING:         Adds the prometheus monitoring resources
 #   MONITORING_PORT:    Set a custom monitoring port
 #   HEALTH_PORT:        Set a custom health port
+#   LOGGING_LEVEL:      Set the level of logging [info|debug]
 #   DRY_RUN:            Prints the resources to be applied instead of applying them
 #
-operator: check-admin have-platform kustomize kubectl .operator-port-patch
+operator: check-admin have-platform kustomize kubectl .operator-port-patch .operator-log-level-patch
 ifeq ($(MONITORING), true)
 	@$(MAKE) -s .operator-can-monitor
 	@$(call add-remove-operator-monitoring,$@,add)
@@ -289,6 +299,14 @@ ifneq ($(MONITORING_PORT), 8080)
 else ifneq ($(HEALTH_PORT), 8081)
 	@$(call add-remove-kind-patch,$(MANAGER),add,$(PORTS_PATCH).$(YAML),Deployment)
 endif
+# Set the Log level of the operator
+ifneq ($(LOGGING_LEVEL), info)
+	@$(call add-remove-kind-patch,$(MANAGER),add,$(LOG_LEVEL_PATCH).$(YAML),Deployment)
+else ifneq ($(LOGGING_LEVEL), 0)
+		@$(call add-remove-kind-patch,$(MANAGER),add,$(LOG_LEVEL_PATCH).$(YAML),Deployment)
+else
+	@$(call add-remove-kind-patch,$(MANAGER),remove,$(LOG_LEVEL_PATCH).$(YAML),Deployment)
+endif
 ifeq ($(DRY_RUN), false)
 	@$(KUSTOMIZE) build $(KOPTIONS) $@ | kubectl apply -f -
 else
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index 4adf46cc9..89d36621a 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -142,6 +142,7 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) (*cobra.Command, *installCmdO
 	cmd.Flags().StringArray("node-selector", nil, "Add a NodeSelector to the operator Pod")
 	cmd.Flags().StringArray("operator-resources", nil, "Define the resources requests and limits assigned to the operator Pod as <requestType.requestResource=value> (i.e., limits.memory=256Mi)")
 	cmd.Flags().StringArray("operator-env-vars", nil, "Add an environment variable to set in the operator Pod(s), as <name=value>")
+	cmd.Flags().StringP("log-level", "z", "info", "The level of operator logging (default - info): info or 0, debug or 1")
 
 	// save
 	cmd.Flags().Bool("save", false, "Save the install parameters into the default kamel configuration file (kamel-config.yaml)")
@@ -195,6 +196,7 @@ type installCmdOptions struct {
 	Tolerations              []string `mapstructure:"tolerations"`
 	NodeSelectors            []string `mapstructure:"node-selectors"`
 	ResourcesRequirements    []string `mapstructure:"operator-resources"`
+	LogLevel                 string   `mapstructure:"log-level"`
 	EnvVars                  []string `mapstructure:"operator-env-vars"`
 
 	registry         v1.RegistrySpec
@@ -219,6 +221,11 @@ func (o *installCmdOptions) install(cobraCmd *cobra.Command, _ []string) error {
 		o.EnvVars = append(o.EnvVars, "KAMEL_INSTALL_DEFAULT_KAMELETS=false")
 	}
 
+	// Set the log-level
+	if len(o.LogLevel) > 0 {
+		o.EnvVars = append(o.EnvVars, fmt.Sprintf("LOG_LEVEL=%s", strings.TrimSpace(o.LogLevel)))
+	}
+
 	installViaOLM := false
 	if o.Olm {
 		var err error
diff --git a/pkg/cmd/install_test.go b/pkg/cmd/install_test.go
index 874b1538c..c1aaaee59 100644
--- a/pkg/cmd/install_test.go
+++ b/pkg/cmd/install_test.go
@@ -393,3 +393,66 @@ func TestInstallMavenExtension(t *testing.T) {
 	assert.Nil(t, err)
 	assert.Equal(t, "fi.yle.tools:aws-maven:1.4.2", installCmdOptions.MavenExtensions[0])
 }
+
+func TestInstallInfoLogging(t *testing.T) {
+	installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdInstall)
+	assert.Nil(t, err)
+	assert.Equal(t, "info", installCmdOptions.LogLevel)
+}
+
+func TestInstallInfoLogging1(t *testing.T) {
+	installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdInstall, "-z", "0")
+	assert.Nil(t, err)
+	assert.Equal(t, "0", installCmdOptions.LogLevel)
+}
+
+func TestInstallInfoLogging2(t *testing.T) {
+	installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdInstall, "--log-level", "0")
+	assert.Nil(t, err)
+	assert.Equal(t, "0", installCmdOptions.LogLevel)
+}
+
+func TestInstallInfoLogging3(t *testing.T) {
+	installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdInstall, "-z", "info")
+	assert.Nil(t, err)
+	assert.Equal(t, "info", installCmdOptions.LogLevel)
+}
+
+func TestInstallInfoLogging4(t *testing.T) {
+	installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdInstall, "--log-level", "info")
+	assert.Nil(t, err)
+	assert.Equal(t, "info", installCmdOptions.LogLevel)
+}
+
+func TestInstallDebugLogging1(t *testing.T) {
+	installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdInstall, "-z", "1")
+	assert.Nil(t, err)
+	assert.Equal(t, "1", installCmdOptions.LogLevel)
+}
+
+func TestInstallDebugLogging2(t *testing.T) {
+	installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdInstall, "--log-level", "1")
+	assert.Nil(t, err)
+	assert.Equal(t, "1", installCmdOptions.LogLevel)
+}
+
+func TestInstallDebugLogging3(t *testing.T) {
+	installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdInstall, "-z", "debug")
+	assert.Nil(t, err)
+	assert.Equal(t, "debug", installCmdOptions.LogLevel)
+}
+
+func TestInstallDebugLogging4(t *testing.T) {
+	installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+	_, err := test.ExecuteCommand(rootCmd, cmdInstall, "--log-level", "debug")
+	assert.Nil(t, err)
+	assert.Equal(t, "debug", installCmdOptions.LogLevel)
+}
diff --git a/pkg/cmd/operator/operator.go b/pkg/cmd/operator/operator.go
index c58db2d00..5b5bc29d7 100644
--- a/pkg/cmd/operator/operator.go
+++ b/pkg/cmd/operator/operator.go
@@ -25,6 +25,7 @@ import (
 	"os"
 	"runtime"
 	"strconv"
+	"strings"
 	"time"
 
 	"go.uber.org/automaxprocs/maxprocs"
@@ -39,8 +40,9 @@ import (
 	"k8s.io/apimachinery/pkg/selection"
 	"k8s.io/client-go/tools/leaderelection/resourcelock"
 	"k8s.io/client-go/tools/record"
-	"k8s.io/klog/v2"
+	klog "k8s.io/klog/v2"
 
+	"go.uber.org/zap/zapcore"
 	"sigs.k8s.io/controller-runtime/pkg/cache"
 	ctrl "sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/config"
@@ -61,9 +63,11 @@ import (
 	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/defaults"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	camelLog "github.com/apache/camel-k/pkg/util/log"
 )
 
 var log = logf.Log.WithName("cmd")
+var camLog = camelLog.Log.WithName("log")
 
 func printVersion() {
 	log.Info(fmt.Sprintf("Go Version: %s", runtime.Version()))
@@ -73,6 +77,17 @@ func printVersion() {
 	log.Info(fmt.Sprintf("Camel K Operator Version: %v", defaults.Version))
 	log.Info(fmt.Sprintf("Camel K Default Runtime Version: %v", defaults.DefaultRuntimeVersion))
 	log.Info(fmt.Sprintf("Camel K Git Commit: %v", defaults.GitCommit))
+
+	// Will only appear if DEBUG level has been enabled using the env var LOG_LEVEL
+	camLog.Debug("*** DEBUG level messages will be logged ***")
+}
+
+type loglvl struct {
+	Level zapcore.Level
+}
+
+func (l loglvl) Enabled(lvl zapcore.Level) bool {
+	return l.Level.Enabled(lvl)
 }
 
 // Run starts the Camel K operator.
@@ -85,8 +100,33 @@ func Run(healthPort, monitoringPort int32, leaderElection bool, leaderElectionID
 	// implementing the logr.Logger interface. This logger will
 	// be propagated through the whole operator, generating
 	// uniform and structured logs.
+
+	// The constants specified here are zap specific
+	var logLevel zapcore.Level
+	logLevelVal, ok := os.LookupEnv("LOG_LEVEL")
+	if ok {
+		switch strings.ToLower(logLevelVal) {
+		case "error":
+			logLevel = zapcore.ErrorLevel
+		case "info":
+			logLevel = zapcore.InfoLevel
+		case "debug":
+			logLevel = zapcore.DebugLevel
+		default:
+			customLevel, err := strconv.Atoi(strings.ToLower(logLevelVal))
+			exitOnError(err, "Invalid log-level")
+			// Need to multiply by -1 to turn logr expected level into zap level
+			logLevel = zapcore.Level(int8(customLevel) * -1)
+		}
+	}
+
+	logLevelEnabler := loglvl{
+		Level: logLevel,
+	}
+
 	logf.SetLogger(zap.New(func(o *zap.Options) {
 		o.Development = false
+		o.Level = logLevelEnabler
 	}))
 
 	klog.SetLogger(log)
diff --git a/pkg/controller/integration/build_kit.go b/pkg/controller/integration/build_kit.go
index 3acd1e0a6..a08ff0bba 100644
--- a/pkg/controller/integration/build_kit.go
+++ b/pkg/controller/integration/build_kit.go
@@ -63,8 +63,8 @@ func (action *buildKitAction) Handle(ctx context.Context, integration *v1.Integr
 	// IntegrationKit may be nil if its being upgraded
 	//
 	if integration.Status.IntegrationKit != nil {
-
 		// IntegrationKit fully defined so find it
+		action.L.Debugf("Finding integration kit %s for integration %s\n", integration.Status.IntegrationKit.Name, integration.Name)
 		kit, err := kubernetes.GetIntegrationKit(ctx, action.client, integration.Status.IntegrationKit.Name, integration.Status.IntegrationKit.Namespace)
 		if err != nil {
 			return nil, errors.Wrapf(err, "unable to find integration kit %s/%s, %s", integration.Status.IntegrationKit.Namespace, integration.Status.IntegrationKit.Name, err)
@@ -73,14 +73,16 @@ func (action *buildKitAction) Handle(ctx context.Context, integration *v1.Integr
 		if kit.Labels[v1.IntegrationKitTypeLabel] == v1.IntegrationKitTypePlatform {
 			match, err := integrationMatches(integration, kit)
 			if err != nil {
-				return nil, err
+				return nil, errors.Wrapf(err, "unable to match any integration kit with integration %s/%s", integration.Namespace, integration.Name)
 			} else if !match {
 				// We need to re-generate a kit, or search for a new one that
 				// matches the integration, so let's remove the association
 				// with the kit.
+
 				//
 				// All tests & conditionals check for a nil assignment
 				//
+				action.L.Debug("No match found between integration and integrationkit. Resetting integration's integrationkit to empty", "integration", integration.Name, "integrationkit", integration.Status.IntegrationKit.Name, "namespace", integration.Namespace)
 				integration.SetIntegrationKit(nil)
 				return integration, nil
 			}
@@ -101,38 +103,49 @@ func (action *buildKitAction) Handle(ctx context.Context, integration *v1.Integr
 		return nil, nil
 	}
 
+	action.L.Debug("No kit specified in integration status so looking up", "integration", integration.Name, "namespace", integration.Namespace)
 	existingKits, err := lookupKitsForIntegration(ctx, action.client, integration)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrapf(err, "failed to lookup kits for integration %s/%s", integration.Namespace, integration.Name)
 	}
 
+	action.L.Debug("Applying traits to integration", "integration", integration.Name, "namespace", integration.Namespace)
 	env, err := trait.Apply(ctx, action.client, integration, nil)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrapf(err, "failed to apply traits to integration %s/%s", integration.Namespace, integration.Name)
 	}
 
+	action.L.Debug("Searching integration kits to assign to integration", "integration", integration.Name, "namespace", integration.Namespace)
 	var integrationKit *v1.IntegrationKit
 kits:
 	for _, kit := range env.IntegrationKits {
 		kit := kit
+
 		for i := range existingKits {
 			k := &existingKits[i]
+
+			action.L.Debug("Comparing existing kit with environment", "env kit", kit.Name, "existing kit", k.Name)
 			match, err := kitMatches(&kit, k)
 			if err != nil {
-				return nil, err
+				return nil, errors.Wrapf(err, "error occurred matches integration kits with environment for integration %s/%s", integration.Namespace, integration.Name)
 			}
 			if match {
 				if integrationKit == nil ||
 					integrationKit.Status.Phase != v1.IntegrationKitPhaseReady && k.Status.Phase == v1.IntegrationKitPhaseReady ||
 					integrationKit.Status.Phase == v1.IntegrationKitPhaseReady && k.Status.Phase == v1.IntegrationKitPhaseReady && k.HasHigherPriorityThan(integrationKit) {
 					integrationKit = k
+					action.L.Debug("Found matching kit", "integration kit", integrationKit.Name)
 				}
 
 				continue kits
+			} else {
+				action.L.Debug("Cannot match kits", "env kit", kit.Name, "existing kit", k.Name)
 			}
 		}
+
+		action.L.Debug("No existing kit available for integration. Creating a new one.", "integration", integration.Name, "namespace", integration.Namespace, "integration kit", kit.Name)
 		if err := action.client.Create(ctx, &kit); err != nil {
-			return nil, err
+			return nil, errors.Wrapf(err, "failed to create new integration kit for integration %s/%s", integration.Namespace, integration.Name)
 		}
 		if integrationKit == nil {
 			integrationKit = &kit
@@ -140,12 +153,16 @@ kits:
 	}
 
 	if integrationKit != nil {
+
+		action.L.Debug("Setting integration kit for integration", "integration", integration.Name, "namespace", integration.Namespace, "integration kit", integrationKit.Name)
 		// Set the kit name so the next handle loop, will fall through the
 		// same path as integration with a user defined kit
 		integration.SetIntegrationKit(integrationKit)
 		if integrationKit.Status.Phase == v1.IntegrationKitPhaseReady {
 			integration.Status.Phase = v1.IntegrationPhaseDeploying
 		}
+	} else {
+		action.L.Debug("Not yet able to assign an integration kit to integration", "integration", integration.Name, "namespace", integration.Namespace)
 	}
 
 	return integration, nil
diff --git a/pkg/controller/integration/integration_controller.go b/pkg/controller/integration/integration_controller.go
index 4700c48d6..729db0ca3 100644
--- a/pkg/controller/integration/integration_controller.go
+++ b/pkg/controller/integration/integration_controller.go
@@ -141,6 +141,8 @@ func add(mgr manager.Manager, c client.Client, r reconcile.Reconciler) error {
 
 				for i := range list.Items {
 					integration := &list.Items[i]
+					log.Debug("Integration Controller: Assessing integration", "integration", integration.Name, "namespace", integration.Namespace)
+
 					if match, err := integrationMatches(integration, kit); err != nil {
 						log.Errorf(err, "Error matching integration %q with kit %q", integration.Name, kit.Name)
 
diff --git a/pkg/controller/integration/kits.go b/pkg/controller/integration/kits.go
index 6994ab206..283104065 100644
--- a/pkg/controller/integration/kits.go
+++ b/pkg/controller/integration/kits.go
@@ -33,6 +33,7 @@ import (
 	"github.com/apache/camel-k/pkg/trait"
 	"github.com/apache/camel-k/pkg/util"
 	"github.com/apache/camel-k/pkg/util/defaults"
+	"github.com/apache/camel-k/pkg/util/log"
 )
 
 func lookupKitsForIntegration(ctx context.Context, c ctrl.Reader, integration *v1.Integration, options ...ctrl.ListOption) ([]v1.IntegrationKit, error) {
@@ -83,19 +84,27 @@ func lookupKitsForIntegration(ctx context.Context, c ctrl.Reader, integration *v
 
 // integrationMatches returns whether the v1.IntegrationKit meets the requirements of the v1.Integration.
 func integrationMatches(integration *v1.Integration, kit *v1.IntegrationKit) (bool, error) {
+	ilog := log.ForIntegration(integration)
+
+	ilog.Debug("Matching integration", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace)
 	if kit.Status.Phase == v1.IntegrationKitPhaseError {
+		ilog.Debug("Integration kit has a phase of Error", "integration-kit", kit.Name, "namespace", integration.Namespace)
 		return false, nil
 	}
 	if kit.Status.Version != integration.Status.Version {
+		ilog.Debug("Integration and integration-kit versions do not match", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace)
 		return false, nil
 	}
 	if kit.Status.RuntimeProvider != integration.Status.RuntimeProvider {
+		ilog.Debug("Integration and integration-kit runtime providers do not match", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace)
 		return false, nil
 	}
 	if kit.Status.RuntimeVersion != integration.Status.RuntimeVersion {
+		ilog.Debug("Integration and integration-kit runtime versions do not match", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace)
 		return false, nil
 	}
 	if len(integration.Status.Dependencies) != len(kit.Spec.Dependencies) {
+		ilog.Debug("Integration and integration-kit have different number of dependencies", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace)
 		return false, nil
 	}
 	// When a platform kit is created it inherits the traits from the integrations and as
@@ -109,11 +118,15 @@ func integrationMatches(integration *v1.Integration, kit *v1.IntegrationKit) (bo
 	// A kit can be used only if it contains a subset of the traits and related configurations
 	// declared on integration.
 	if match, err := hasMatchingTraits(integration.Spec.Traits, kit.Spec.Traits); !match || err != nil {
+		ilog.Debug("Integration and integration-kit traits do not match", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace)
 		return false, err
 	}
 	if !util.StringSliceContains(kit.Spec.Dependencies, integration.Status.Dependencies) {
+		ilog.Debug("Integration and integration-kit dependencies do not match", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace)
 		return false, nil
 	}
+
+	ilog.Debug("Matched Integration and integration-kit", "integration", integration.Name, "integration-kit", kit.Name, "namespace", integration.Namespace)
 	return true, nil
 }
 
diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go
index f1ec667ba..848ed4981 100644
--- a/pkg/resources/resources.go
+++ b/pkg/resources/resources.go
@@ -188,6 +188,13 @@ var assets = func() http.FileSystem {
 
 			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x53\x4d\x6f\xeb\x38\x0c\xbc\xeb\x57\x0c\xe2\x4b\x0b\xe4\x63\x77\x8f\xd9\x93\xb7\x4d\xb0\xc6\x16\x49\x51\xa7\x5b\xf4\xc8\xd8\x8c\x4d\x54\x96\xf4\x24\xb9\x6e\xfe\xfd\x83\x9c\xa4\x1f\x78\x78\xb7\xf2\x64\x4b\xd4\x70\x86\x43\x66\x98\x7d\x5f\xa8\x0c\x77\x52\xb1\x09\x5c\x23\x5a\xc4\x96\x91\x3b\xaa\x5a\x46\x69\x0f\x71\x20\xcf\x58\xdb\xde\xd4\x14\xc5\x1a\x5c\xe5\xe5\xfa\x1a\xbd\xa9\xd9\xc3\x1a\x86\xf5\xe8\xac\x67\x95\xa1\x [...]
 		},
+		"/manager/patch-log-level.yaml": &vfsgen۰CompressedFileInfo{
+			name:             "patch-log-level.yaml",
+			modTime:          time.Time{},
+			uncompressedSize: 1041,
+
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x53\x41\x6e\xdb\x30\x10\xbc\xf3\x15\x03\xeb\x92\x00\xb6\x15\xf4\xe8\x9e\xdc\xc4\x6e\x85\x1a\x16\x10\x39\x09\x72\x2a\x68\x69\x2d\x2d\x2a\x71\x59\x92\x8a\xe2\xdf\x17\x94\xed\x26\x41\xaf\xe1\x8d\xe2\x6a\x67\x66\x67\x36\xc1\xec\xf3\x8e\x4a\xb0\xe1\x92\x8c\xa7\x0a\x41\x10\x1a\xc2\xd2\xea\xb2\x21\x14\x72\x08\x83\x76\x84\xb5\xf4\xa6\xd2\x81\xc5\xe0\x6a\x59\xac\xaf\xd1\x9b\x8a\x1c\xc4\x10\xc4\xa1\x13\x47\x2a\x41\x29\x26\x [...]
+		},
 		"/manager/patch-node-selector.yaml": &vfsgen۰CompressedFileInfo{
 			name:             "patch-node-selector.yaml",
 			modTime:          time.Time{},
@@ -632,6 +639,7 @@ var assets = func() http.FileSystem {
 		fs["/manager/operator-deployment.yaml"].(os.FileInfo),
 		fs["/manager/operator-service-account.yaml"].(os.FileInfo),
 		fs["/manager/patch-image-pull-policy-always.yaml"].(os.FileInfo),
+		fs["/manager/patch-log-level.yaml"].(os.FileInfo),
 		fs["/manager/patch-node-selector.yaml"].(os.FileInfo),
 		fs["/manager/patch-ports.yaml"].(os.FileInfo),
 		fs["/manager/patch-resource-requirements.yaml"].(os.FileInfo),
@@ -724,6 +732,11 @@ func (fs vfsgen۰FS) Open(path string) (http.File, error) {
 			vfsgen۰CompressedFileInfo: f,
 			gr:                        gr,
 		}, nil
+	case *vfsgen۰FileInfo:
+		return &vfsgen۰File{
+			vfsgen۰FileInfo: f,
+			Reader:          bytes.NewReader(f.content),
+		}, nil
 	case *vfsgen۰DirInfo:
 		return &vfsgen۰Dir{
 			vfsgen۰DirInfo: f,
@@ -805,6 +818,37 @@ func (f *vfsgen۰CompressedFile) Close() error {
 	return f.gr.Close()
 }
 
+// vfsgen۰FileInfo is a static definition of an uncompressed file (because it's not worth gzip compressing).
+type vfsgen۰FileInfo struct {
+	name    string
+	modTime time.Time
+	content []byte
+}
+
+func (f *vfsgen۰FileInfo) Readdir(count int) ([]os.FileInfo, error) {
+	return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
+}
+func (f *vfsgen۰FileInfo) Stat() (os.FileInfo, error) { return f, nil }
+
+func (f *vfsgen۰FileInfo) NotWorthGzipCompressing() {}
+
+func (f *vfsgen۰FileInfo) Name() string       { return f.name }
+func (f *vfsgen۰FileInfo) Size() int64        { return int64(len(f.content)) }
+func (f *vfsgen۰FileInfo) Mode() os.FileMode  { return 0444 }
+func (f *vfsgen۰FileInfo) ModTime() time.Time { return f.modTime }
+func (f *vfsgen۰FileInfo) IsDir() bool        { return false }
+func (f *vfsgen۰FileInfo) Sys() interface{}   { return nil }
+
+// vfsgen۰File is an opened file instance.
+type vfsgen۰File struct {
+	*vfsgen۰FileInfo
+	*bytes.Reader
+}
+
+func (f *vfsgen۰File) Close() error {
+	return nil
+}
+
 // vfsgen۰DirInfo is a static definition of a directory.
 type vfsgen۰DirInfo struct {
 	name    string
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index ca5fa5050..0158b78a4 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -29,13 +29,23 @@ import (
 	"github.com/apache/camel-k/pkg/client"
 	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/util/log"
 	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 func Apply(ctx context.Context, c client.Client, integration *v1.Integration, kit *v1.IntegrationKit) (*Environment, error) {
+	var ilog log.Logger
+	if integration != nil {
+		ilog = log.ForIntegration(integration)
+	} else if kit != nil {
+		ilog = log.ForIntegrationKit(kit)
+	} else {
+		ilog = log.WithValues("Function", "trait.Apply")
+	}
+
 	environment, err := newEnvironment(ctx, c, integration, kit)
 	if err != nil {
-		return nil, err
+		return nil, errors.Wrap(err, "error creating trait environment")
 	}
 
 	catalog := NewCatalog(c)
@@ -56,6 +66,13 @@ func Apply(ctx context.Context, c client.Client, integration *v1.Integration, ki
 		}
 	}
 
+	if integration != nil {
+		ilog.Debug("Applied traits to Integration", "integration", integration.Name, "namespace", integration.Namespace)
+	} else if kit != nil {
+		ilog.Debug("Applied traits to Integration kit", "integration kit", kit.Name, "namespace", kit.Namespace)
+	} else {
+		ilog.Debug("Applied traits")
+	}
 	return environment, nil
 }