Skip to content

Commit f513f11

Browse files
committed
doc/dev/logging.md: discuss how to use logging in SDK operators
1 parent 9481391 commit f513f11

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

doc/dev/logging.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Logging in operators
2+
3+
Operator SDK-generated operators use the [`logr`][godoc_logr] interface to log. This log interface has several backends such as [`zap`][repo_zapr], which the SDK uses in generated code by default. [`logr.Logger`][godoc_logr_logger] exposes [structured logging][site_struct_logging] methods that help create machine-readable logs and adding a wealth of information to log records.
4+
5+
## Setting the logger
6+
7+
Operators set the logger for all operator logging in [`cmd/manager/main.go`][code_set_logger]:
8+
9+
```Go
10+
import (
11+
"github.com/operator-framework/operator-sdk/pkg/logutil"
12+
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
13+
)
14+
15+
func main() {
16+
logf.SetLogger(logf.ZapLogger(false))
17+
log := logutil.SDKLog.WithName("cmd")
18+
19+
...
20+
21+
log.Info("Starting the Cmd.")
22+
23+
...
24+
}
25+
```
26+
27+
By using `controller-runtime/pkg/runtime/log`, your logger is propagated through `controller-runtime`. Any logs produced by `controller-runtime` code will be through your logger, and therefore have the same formatting and destination.
28+
29+
In the above example, `logf.ZapLogger()` takes a boolean flag to set development parameters. Passing in `true` will set the logger to log in development mode; debug log statements will trigger, and error log statements will include stack traces.
30+
31+
## Creating a structured log statement
32+
33+
There are two ways to create structured logs with `logr`. You can create new loggers using `log.WithValues(keyValues)` that include `keyValues`, a list of key-value pair `interface{}`'s, in each log record. Alternatively you can include `keyValues` directly in a log statement, as all `logr` log statements take some message and `keyValues`. The signature of `logr.Error()` has an `error`-type parameter, which can be `nil`.
34+
35+
An example from [`memcached_controller.go`][code_memcached_controller]:
36+
37+
```Go
38+
package memcached
39+
40+
// Set a global logger for the memcached package. Each log record produced
41+
// by this logger will have an identifier containing "controller_memcached".
42+
// These names are hierarchical; the name attached to memcached log statements
43+
// will be "operator-sdk.controller_memcached" because SDKLog has name
44+
// "operator-sdk".
45+
var log = logutil.SDKLog.WithName("controller_memcached")
46+
47+
func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Result, error) {
48+
// Create a logger for Reconcile() that includes "Request.Namespace"
49+
// and "Request.Name" in each log record from this log statement.
50+
reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
51+
reqLogger.Info("Reconciling Memcached.")
52+
53+
memcached := &cachev1alpha1.Memcached{}
54+
err := r.client.Get(context.TODO(), request.NamespacedName, memcached)
55+
if err != nil {
56+
if errors.IsNotFound(err) {
57+
reqLogger.Info("Memcached resource not found. Ignoring since object must be deleted.")
58+
return reconcile.Result{}, nil
59+
}
60+
return reconcile.Result{}, err
61+
}
62+
63+
found := &appsv1.Deployment{}
64+
err = r.client.Get(context.TODO(), types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found)
65+
if err != nil {
66+
if errors.IsNotFound(err) {
67+
dep := r.deploymentForMemcached(memcached)
68+
// Include "Deployment.Namespace" and "Deployment.Name" in records
69+
// produced by this particular log statement. "Request.Namespace" and
70+
// "Request.Name" will also be included from reqLogger.
71+
reqLogger.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
72+
err = r.client.Create(context.TODO(), dep)
73+
if err != nil {
74+
// Include the error in records produced by this log statement.
75+
reqLogger.Error(err, "failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
76+
return reconcile.Result{}, err
77+
}
78+
}
79+
return reconcile.Result{}, err
80+
}
81+
82+
...
83+
}
84+
```
85+
86+
## Non-default logging
87+
88+
If you do not want to use `logr` as your logging tool, you can remove `logr`-specific statements without issue from your operator's code, including the `logr` [setup code][code_set_logger] in `cmd/manager/main.go`, and add your own. Note that removing `logr` setup code will prevent `controller-runtime` from logging.
89+
90+
91+
[godoc_logr]:https://godoc.org/github.com/go-logr/logr
92+
[repo_zapr]:https://godoc.org/github.com/go-logr/zapr
93+
[godoc_logr_logger]:https://godoc.org/github.com/go-logr/logr#Logger
94+
[site_struct_logging]:https://www.client9.com/structured-logging-in-golang/
95+
[code_memcached_controller]:../../example/memcached-operator/memcached_controller.go.tmpl
96+
[code_set_logger]:https://github.com/operator-framework/operator-sdk/blob/948139171fff0e802c9e68f87cb95939941772ef/pkg/scaffold/cmd.go#L68-L72

example/memcached-operator/memcached_controller.go.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ type ReconcileMemcached struct {
8787
// The Controller will requeue the Request to be processed again if the returned error is non-nil or
8888
// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
8989
func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Result, error) {
90-
reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Requst.Name", request.Name)
90+
reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
9191
reqLogger.Info("Reconciling Memcached")
9292

9393
// Fetch the Memcached instance

0 commit comments

Comments
 (0)