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 }