github.com/tototoman/operator-sdk@v0.8.2/doc/user/logging.md (about)

     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/log/zap"
    12  	"github.com/spf13/pflag"
    13  	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
    14  )
    15  
    16  func main() {
    17    pflag.CommandLine.AddFlagSet(zap.FlagSet())
    18    pflag.Parse()
    19    logf.SetLogger(zap.Logger())
    20    log := logf.Log.WithName("cmd")
    21  
    22    ...
    23  
    24    log.Info("Starting the Cmd.")
    25  
    26    ...
    27  }
    28  ```
    29  
    30  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.
    31  
    32  ### Default zap logger
    33  
    34  Operator SDK uses a `zap`-based `logr` backend when scaffolding new projects. To assist with configuring and using this logger, the SDK includes several helper functions.
    35  
    36  In the above example, we add the zap flagset to the operator's command line flags with `zap.FlagSet()`, and then set the controller-runtime logger with `zap.Logger()`.
    37  
    38  By default, `zap.Logger()` will return a logger that is ready for production use. It uses a JSON encoder, logs starting at the `info` level, and has [sampling][zap_sampling] enabled. To customize the default behavior, users can use the zap flagset and specify flags on the command line. The zap flagset includes the following flags that can be used to configure the logger:
    39  * `--zap-devel` - Enables the zap development config (changes defaults to console encoder, debug log level, and disables sampling) (default: `false`)
    40  * `--zap-encoder` string - Sets the zap log encoding (`json` or `console`)
    41  * `--zap-level` string or integer - Sets the zap log level (`debug`, `info`, `error`, or an integer value greater than 0). If 4 or greater the verbosity of client-go will be set to this level.
    42  * `--zap-sample` - Enables zap's sampling mode. Sampling will be disabled for integer log levels greater than 1.
    43  
    44  
    45  ## Creating a structured log statement
    46  
    47  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`.
    48  
    49  An example from [`memcached_controller.go`][code_memcached_controller]:
    50  
    51  ```Go
    52  package memcached
    53  
    54  import (
    55  	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
    56  )
    57  
    58  // Set a global logger for the memcached package. Each log record produced
    59  // by this logger will have an identifier containing "controller_memcached".
    60  // These names are hierarchical; the name attached to memcached log statements
    61  // will be "operator-sdk.controller_memcached" because SDKLog has name
    62  // "operator-sdk".
    63  var log = logf.Log.WithName("controller_memcached")
    64  
    65  func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Result, error) {
    66    // Create a logger for Reconcile() that includes "Request.Namespace"
    67    // and "Request.Name" in each log record from this log statement.
    68    reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
    69    reqLogger.Info("Reconciling Memcached.")
    70  
    71    memcached := &cachev1alpha1.Memcached{}
    72    err := r.client.Get(context.TODO(), request.NamespacedName, memcached)
    73    if err != nil {
    74      if errors.IsNotFound(err) {
    75        reqLogger.Info("Memcached resource not found. Ignoring since object must be deleted.")
    76        return reconcile.Result{}, nil
    77      }
    78      return reconcile.Result{}, err
    79    }
    80  
    81    found := &appsv1.Deployment{}
    82    err = r.client.Get(context.TODO(), types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found)
    83    if err != nil {
    84      if errors.IsNotFound(err) {
    85        dep := r.deploymentForMemcached(memcached)
    86        // Include "Deployment.Namespace" and "Deployment.Name" in records
    87        // produced by this particular log statement. "Request.Namespace" and
    88        // "Request.Name" will also be included from reqLogger.
    89        reqLogger.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
    90        err = r.client.Create(context.TODO(), dep)
    91        if err != nil {
    92          // Include the error in records produced by this log statement.
    93          reqLogger.Error(err, "Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
    94          return reconcile.Result{}, err
    95        }
    96      }
    97      return reconcile.Result{}, err
    98    }
    99  
   100    ...
   101  }
   102  ```
   103  
   104  Log records will look like the following (from `reqLogger.Error()` above):
   105  
   106  ```
   107  2018-11-08T00:00:25.700Z	ERROR	operator-sdk.controller_memcached pkg/controller/memcached/memcached_controller.go:118	Failed to create new Deployment	{"Request.Namespace", "memcached", "Request.Name", "memcached-operator", "Deployment.Namespace", "memcached", "Deployment.Name", "memcached-operator"}
   108  ```
   109  
   110  ## Non-default logging
   111  
   112  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.
   113  
   114  
   115  [godoc_logr]:https://godoc.org/github.com/go-logr/logr
   116  [repo_zapr]:https://godoc.org/github.com/go-logr/zapr
   117  [godoc_logr_logger]:https://godoc.org/github.com/go-logr/logr#Logger
   118  [site_struct_logging]:https://www.client9.com/structured-logging-in-golang/
   119  [code_memcached_controller]:../../example/memcached-operator/memcached_controller.go.tmpl
   120  [code_set_logger]:https://github.com/operator-framework/operator-sdk/blob/4d66be409a69d169aaa29d470242a1defbaf08bb/internal/pkg/scaffold/cmd.go#L92-L96
   121  [zap_sampling]:https://github.com/uber-go/zap/blob/master/FAQ.md#why-sample-application-logs