sigs.k8s.io/controller-runtime@v0.18.2/TMP-LOGGING.md (about) 1 Logging Guidelines 2 ================== 3 4 controller-runtime uses a kind of logging called *structured logging*. If 5 you've used a library like Zap or logrus before, you'll be familiar with 6 the concepts we use. If you've only used a logging library like the "log" 7 package (in the Go standard library) or "glog" (in Kubernetes), you'll 8 need to adjust how you think about logging a bit. 9 10 ### Getting Started With Structured Logging 11 12 With structured logging, we associate a *constant* log message with some 13 variable key-value pairs. For instance, suppose we wanted to log that we 14 were starting reconciliation on a pod. In the Go standard library logger, 15 we might write: 16 17 ```go 18 log.Printf("starting reconciliation for pod %s/%s", podNamespace, podName) 19 ``` 20 21 In controller-runtime, we'd instead write: 22 23 ```go 24 logger.Info("starting reconciliation", "pod", req.NamespacedName) 25 ``` 26 27 or even write 28 29 ```go 30 func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Response, error) { 31 logger := logger.WithValues("pod", req.NamespacedName) 32 // do some stuff 33 logger.Info("starting reconciliation") 34 } 35 ``` 36 37 Notice how we've broken out the information that we want to convey into 38 a constant message (`"starting reconciliation"`) and some key-value pairs 39 that convey variable information (`"pod", req.NamespacedName`). We've 40 there-by added "structure" to our logs, which makes them easier to save 41 and search later, as well as correlate with metrics and events. 42 43 All of controller-runtime's logging is done via 44 [logr](https://github.com/go-logr/logr), a generic interface for 45 structured logging. You can use whichever logging library you want to 46 implement the actual mechanics of the logging. controller-runtime 47 provides some helpers to make it easy to use 48 [Zap](https://go.uber.org/zap) as the implementation. 49 50 You can configure the logging implementation using 51 `"sigs.k8s.io/controller-runtime/pkg/log".SetLogger`. That 52 package also contains the convenience functions for setting up Zap. 53 54 You can get a handle to the "root" logger using 55 `"sigs.k8s.io/controller-runtime/pkg/log".Log`, and can then call 56 `WithName` to create individual named loggers. You can call `WithName` 57 repeatedly to chain names together: 58 59 ```go 60 logger := log.Log.WithName("controller").WithName("replicaset") 61 // in reconcile... 62 logger = logger.WithValues("replicaset", req.NamespacedName) 63 // later on in reconcile... 64 logger.Info("doing things with pods", "pod", newPod) 65 ``` 66 67 As seen above, you can also call `WithValue` to create a new sub-logger 68 that always attaches some key-value pairs to a logger. 69 70 Finally, you can use `V(1)` to mark a particular log line as "debug" logs: 71 72 ```go 73 logger.V(1).Info("this is particularly verbose!", "state of the world", 74 allKubernetesObjectsEverywhere) 75 ``` 76 77 While it's possible to use higher log levels, it's recommended that you 78 stick with `V(1)` or `V(0)` (which is equivalent to not specifying `V`), 79 and then filter later based on key-value pairs or messages; different 80 numbers tend to lose meaning easily over time, and you'll be left 81 wondering why particular logs lines are at `V(5)` instead of `V(7)`. 82 83 ## Logging errors 84 85 Errors should *always* be logged with `log.Error`, which allows logr 86 implementations to provide special handling of errors (for instance, 87 providing stack traces in debug mode). 88 89 It's acceptable to log call `log.Error` with a nil error object. This 90 conveys that an error occurred in some capacity, but that no actual 91 `error` object was involved. 92 93 Errors returned by the `Reconcile` implementation of the `Reconciler` interface are commonly logged as a `Reconciler error`. 94 It's a developer choice to create an additional error log in the `Reconcile` implementation so a more specific file name and line for the error are returned. 95 96 ## Logging messages 97 98 - Don't put variable content in your messages -- use key-value pairs for 99 that. Never use `fmt.Sprintf` in your message. 100 101 - Try to match the terminology in your messages with your key-value pairs 102 -- for instance, if you have a key-value pairs `api version`, use the 103 term `APIVersion` instead of `GroupVersion` in your message. 104 105 ## Logging Kubernetes Objects 106 107 Kubernetes objects should be logged directly, like `log.Info("this is 108 a Kubernetes object", "pod", somePod)`. controller-runtime provides 109 a special encoder for Zap that will transform Kubernetes objects into 110 `name, namespace, apiVersion, kind` objects, when available and not in 111 development mode. Other logr implementations should implement similar 112 logic. 113 114 ## Logging Structured Values (Key-Value pairs) 115 116 - Use lower-case, space separated keys. For example `object` for objects, 117 `api version` for `APIVersion` 118 119 - Be consistent across your application, and with controller-runtime when 120 possible. 121 122 - Try to be brief but descriptive. 123 124 - Match terminology in keys with terminology in the message. 125 126 - Be careful logging non-Kubernetes objects verbatim if they're very 127 large. 128 129 ### Groups, Versions, and Kinds 130 131 - Kinds should not be logged alone (they're meaningless alone). Use 132 a `GroupKind` object to log them instead, or a `GroupVersionKind` when 133 version is relevant. 134 135 - If you need to log an API version string, use `api version` as the key 136 (formatted as with a `GroupVersion`, or as received directly from API 137 discovery). 138 139 ### Objects and Types 140 141 - If code works with a generic Kubernetes `runtime.Object`, use the 142 `object` key. For specific objects, prefer the resource name as the key 143 (e.g. `pod` for `v1.Pod` objects). 144 145 - For non-Kubernetes objects, the `object` key may also be used, if you 146 accept a generic interface. 147 148 - When logging a raw type, log it using the `type` key, with a value of 149 `fmt.Sprintf("%T", typ)` 150 151 - If there's specific context around a type, the key may be more specific, 152 but should end with `type` -- for instance, `OwnerType` should be logged 153 as `owner` in the context of `log.Error(err, "Could not get ObjectKinds 154 for OwnerType", `owner type`, fmt.Sprintf("%T"))`. When possible, favor 155 communicating kind instead. 156 157 ### Multiple things 158 159 - When logging multiple things, simply pluralize the key. 160 161 ### controller-runtime Specifics 162 163 - Reconcile requests should be logged as `request`, although normal code 164 should favor logging the key. 165 166 - Reconcile keys should be logged as with the same key as if you were 167 logging the object directly (e.g. `log.Info("reconciling pod", "pod", 168 req.NamespacedName)`). This ends up having a similar effect to logging 169 the object directly.