sigs.k8s.io/cluster-api@v1.7.1/internal/log/log.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package log
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/go-logr/logr"
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/klog/v2"
    27  	ctrl "sigs.k8s.io/controller-runtime"
    28  	"sigs.k8s.io/controller-runtime/pkg/client"
    29  
    30  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    31  	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
    32  )
    33  
    34  // LoggerFrom returns a logger with predefined values from a context.Context.
    35  // The logger, when used with controllers, can be expected to contain basic information about the object
    36  // that's being reconciled like:
    37  // - `reconciler group` and `reconciler kind` coming from the For(...) object passed in when building a controller.
    38  // - `name` and `namespace` injected from the reconciliation request.
    39  //
    40  // This is meant to be used with the context supplied in a struct that satisfies the Reconciler interface.
    41  func LoggerFrom(ctx context.Context) Logger {
    42  	log := ctrl.LoggerFrom(ctx)
    43  	return &topologyReconcileLogger{
    44  		// We use call depth 1 so the logger prints the log line of the caller of the log func (e.g. Infof)
    45  		// not of the log func.
    46  		// NOTE: We do this once here, so that we don't have to do this on every log call.
    47  		Logger: log.WithCallDepth(1),
    48  	}
    49  }
    50  
    51  // Logger provides a wrapper to log.Logger to be used for topology reconciler.
    52  type Logger interface {
    53  	// WithObject adds to the logger information about the object being modified by reconcile, which in most case it is
    54  	// a resources being part of the Cluster by reconciled.
    55  	WithObject(obj client.Object) Logger
    56  
    57  	// WithRef adds to the logger information about the object ref being modified by reconcile, which in most case it is
    58  	// a resources being part of the Cluster by reconciled.
    59  	WithRef(ref *corev1.ObjectReference) Logger
    60  
    61  	// WithMachineDeployment adds to the logger information about the MachineDeployment object being processed.
    62  	WithMachineDeployment(md *clusterv1.MachineDeployment) Logger
    63  
    64  	// WithMachinePool adds to the logger information about the MachinePool object being processed.
    65  	WithMachinePool(mp *expv1.MachinePool) Logger
    66  
    67  	// WithValues adds key-value pairs of context to a logger.
    68  	WithValues(keysAndValues ...interface{}) Logger
    69  
    70  	// V returns a logger value for a specific verbosity level, relative to
    71  	// this logger.
    72  	V(level int) Logger
    73  
    74  	// Infof logs to the INFO log.
    75  	// Arguments are handled in the manner of fmt.Printf.
    76  	Infof(msg string, a ...interface{})
    77  
    78  	// Into takes a context and sets the logger as one of its keys.
    79  	//
    80  	// This is meant to be used in reconcilers to enrich the logger within a context with additional values.
    81  	Into(ctx context.Context) (context.Context, Logger)
    82  }
    83  
    84  // topologyReconcileLogger implements Logger.
    85  type topologyReconcileLogger struct {
    86  	logr.Logger
    87  }
    88  
    89  // WithObject adds to the logger information about the object being modified by reconcile, which in most case it is
    90  // a resources being part of the Cluster by reconciled.
    91  func (l *topologyReconcileLogger) WithObject(obj client.Object) Logger {
    92  	return &topologyReconcileLogger{
    93  		Logger: l.Logger.WithValues(
    94  			"resource", metav1.GroupVersionResource{
    95  				Version:  obj.GetObjectKind().GroupVersionKind().Version,
    96  				Group:    obj.GetObjectKind().GroupVersionKind().GroupKind().Group,
    97  				Resource: obj.GetObjectKind().GroupVersionKind().Kind,
    98  			},
    99  			obj.GetObjectKind().GroupVersionKind().Kind, klog.KObj(obj),
   100  		),
   101  	}
   102  }
   103  
   104  // WithRef adds to the logger information about the object ref being modified by reconcile, which in most case it is
   105  // a resources being part of the Cluster by reconciled.
   106  func (l *topologyReconcileLogger) WithRef(ref *corev1.ObjectReference) Logger {
   107  	return &topologyReconcileLogger{
   108  		Logger: l.Logger.WithValues(
   109  			"resource", metav1.GroupVersionResource{
   110  				Version:  ref.GetObjectKind().GroupVersionKind().Version,
   111  				Group:    ref.GetObjectKind().GroupVersionKind().GroupKind().Group,
   112  				Resource: ref.GetObjectKind().GroupVersionKind().Kind,
   113  			},
   114  			ref.GetObjectKind().GroupVersionKind().Kind, klog.KRef(ref.Namespace, ref.Name),
   115  		),
   116  	}
   117  }
   118  
   119  // WithMachineDeployment adds to the logger information about the MachineDeployment object being processed.
   120  func (l *topologyReconcileLogger) WithMachineDeployment(md *clusterv1.MachineDeployment) Logger {
   121  	topologyName := md.Labels[clusterv1.ClusterTopologyMachineDeploymentNameLabel]
   122  	return &topologyReconcileLogger{
   123  		Logger: l.Logger.WithValues(
   124  			"MachineDeployment", klog.KObj(md),
   125  			"MachineDeploymentTopology", topologyName,
   126  		),
   127  	}
   128  }
   129  
   130  // WithMachinePool adds to the logger information about the MachinePool object being processed.
   131  func (l *topologyReconcileLogger) WithMachinePool(mp *expv1.MachinePool) Logger {
   132  	topologyName := mp.Labels[clusterv1.ClusterTopologyMachinePoolNameLabel]
   133  	return &topologyReconcileLogger{
   134  		Logger: l.Logger.WithValues(
   135  			"MachinePool", klog.KObj(mp),
   136  			"MachinePoolTopology", topologyName,
   137  		),
   138  	}
   139  }
   140  
   141  // WithValues adds key-value pairs of context to a logger.
   142  func (l *topologyReconcileLogger) WithValues(keysAndValues ...interface{}) Logger {
   143  	l.Logger = l.Logger.WithValues(keysAndValues...)
   144  	return l
   145  }
   146  
   147  // V returns a logger value for a specific verbosity level, relative to
   148  // this logger.
   149  func (l *topologyReconcileLogger) V(level int) Logger {
   150  	return &topologyReconcileLogger{
   151  		Logger: l.Logger.V(level),
   152  	}
   153  }
   154  
   155  // Infof logs to the INFO log.
   156  // Arguments are handled in the manner of fmt.Printf.
   157  func (l *topologyReconcileLogger) Infof(msg string, a ...interface{}) {
   158  	l.Logger.Info(fmt.Sprintf(msg, a...))
   159  }
   160  
   161  // Into takes a context and sets the logger as one of its keys.
   162  //
   163  // This is meant to be used in reconcilers to enrich the logger within a context with additional values.
   164  func (l *topologyReconcileLogger) Into(ctx context.Context) (context.Context, Logger) {
   165  	return ctrl.LoggerInto(ctx, l.Logger), l
   166  }
   167  
   168  // KObj return a reference to a Kubernetes object in the same format used by kubectl commands (kind/name).
   169  // Note: We're intentionally not using klog.KObj as we want the kind/name format instead of namespace/name.
   170  type KObj struct {
   171  	Obj client.Object
   172  }
   173  
   174  func (ref KObj) String() string {
   175  	if ref.Obj == nil {
   176  		return ""
   177  	}
   178  	return fmt.Sprintf("%s/%s", ref.Obj.GetObjectKind().GroupVersionKind().Kind, ref.Obj.GetName())
   179  }
   180  
   181  // KRef return a reference to a Kubernetes object in the same format used by kubectl commands (kind/name).
   182  type KRef struct {
   183  	Ref *corev1.ObjectReference
   184  }
   185  
   186  func (ref KRef) String() string {
   187  	if ref.Ref == nil {
   188  		return ""
   189  	}
   190  	return fmt.Sprintf("%s/%s", ref.Ref.Kind, ref.Ref.Name)
   191  }