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.