1Logging Guidelines
2==================
3
4controller-runtime uses a kind of logging called *structured logging*. If
5you've used a library like Zap or logrus before, you'll be familiar with
6the concepts we use. If you've only used a logging library like the "log"
7package (in the Go standard library) or "glog" (in Kubernetes), you'll
8need to adjust how you think about logging a bit.
9
10### Getting Started With Structured Logging
11
12With structured logging, we associate a *constant* log message with some
13variable key-value pairs. For instance, suppose we wanted to log that we
14were starting reconciliation on a pod. In the Go standard library logger,
15we might write:
16
17```go
18log.Printf("starting reconciliation for pod %s/%s", podNamespace, podName)
19```
20
21In controller-runtime, we'd instead write:
22
23```go
24logger.Info("starting reconciliation", "pod", req.NamespacedName)
25```
26
27or even write
28
29```go
30func (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
37Notice how we've broken out the information that we want to convey into
38a constant message (`"starting reconciliation"`) and some key-value pairs
39that convey variable information (`"pod", req.NamespacedName`). We've
40there-by added "structure" to our logs, which makes them easier to save
41and search later, as well as correlate with metrics and events.
42
43All of controller-runtime's logging is done via
44[logr](https://github.com/go-logr/logr), a generic interface for
45structured logging. You can use whichever logging library you want to
46implement the actual mechanics of the logging. controller-runtime
47provides some helpers to make it easy to use
48[Zap](https://go.uber.org/zap) as the implementation.
49
50You can configure the logging implementation using
51`"sigs.k8s.io/controller-runtime/pkg/log".SetLogger`. That
52package also contains the convenience functions for setting up Zap.
53
54You 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`
57repeatedly to chain names together:
58
59```go
60logger := log.Log.WithName("controller").WithName("replicaset")
61// in reconcile...
62logger = logger.WithValues("replicaset", req.NamespacedName)
63// later on in reconcile...
64logger.Info("doing things with pods", "pod", newPod)
65```
66
67As seen above, you can also call `WithValue` to create a new sub-logger
68that always attaches some key-value pairs to a logger.
69
70Finally, you can use `V(1)` to mark a particular log line as "debug" logs:
71
72```go
73logger.V(1).Info("this is particularly verbose!", "state of the world",
74allKubernetesObjectsEverywhere)
75```
76
77While it's possible to use higher log levels, it's recommended that you
78stick with `V(1)` or `V(0)` (which is equivalent to not specifying `V`),
79and then filter later based on key-value pairs or messages; different
80numbers tend to lose meaning easily over time, and you'll be left
81wondering why particular logs lines are at `V(5)` instead of `V(7)`.
82
83## Logging errors
84
85Errors should *always* be logged with `log.Error`, which allows logr
86implementations to provide special handling of errors (for instance,
87providing stack traces in debug mode).
88
89It's acceptable to log call `log.Error` with a nil error object. This
90conveys that an error occurred in some capacity, but that no actual
91`error` object was involved.
92
93Errors returned by the `Reconcile` implementation of the `Reconciler` interface are commonly logged as a `Reconciler error`.
94It'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
107Kubernetes objects should be logged directly, like `log.Info("this is
108a Kubernetes object", "pod", somePod)`. controller-runtime provides
109a special encoder for Zap that will transform Kubernetes objects into
110`name, namespace, apiVersion, kind` objects, when available and not in
111development mode. Other logr implementations should implement similar
112logic.
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.
View as plain text