2 Copyright 2018 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
17 // Package log contains utilities for fetching a new logger
18 // when one is not already available.
24 "go.uber.org/zap/buffer"
25 "go.uber.org/zap/zapcore"
26 "k8s.io/apimachinery/pkg/api/meta"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/apimachinery/pkg/types"
31 // KubeAwareEncoder is a Kubernetes-aware Zap Encoder.
32 // Instead of trying to force Kubernetes objects to implement
33 // ObjectMarshaller, we just implement a wrapper around a normal
34 // ObjectMarshaller that checks for Kubernetes objects.
35 type KubeAwareEncoder struct {
36 // Encoder is the zapcore.Encoder that this encoder delegates to
39 // Verbose controls whether or not the full object is printed.
40 // If false, only name, namespace, api version, and kind are printed.
41 // Otherwise, the full object is logged.
45 // namespacedNameWrapper is a zapcore.ObjectMarshaler for Kubernetes NamespacedName
46 type namespacedNameWrapper struct {
50 func (w namespacedNameWrapper) MarshalLogObject(enc zapcore.ObjectEncoder) error {
51 if w.Namespace != "" {
52 enc.AddString("namespace", w.Namespace)
55 enc.AddString("name", w.Name)
60 // kubeObjectWrapper is a zapcore.ObjectMarshaler for Kubernetes objects.
61 type kubeObjectWrapper struct {
65 // MarshalLogObject implements zapcore.ObjectMarshaler
66 func (w kubeObjectWrapper) MarshalLogObject(enc zapcore.ObjectEncoder) error {
67 // TODO(directxman12): log kind and apiversion if not set explicitly (common case)
68 // -- needs an a scheme to convert to the GVK.
69 gvk := w.obj.GetObjectKind().GroupVersionKind()
70 if gvk.Version != "" {
71 enc.AddString("apiVersion", gvk.GroupVersion().String())
72 enc.AddString("kind", gvk.Kind)
75 objMeta, err := meta.Accessor(w.obj)
77 return fmt.Errorf("got runtime.Object without object metadata: %v", w.obj)
80 ns := objMeta.GetNamespace()
82 enc.AddString("namespace", ns)
84 enc.AddString("name", objMeta.GetName())
89 // NB(directxman12): can't just override AddReflected, since the encoder calls AddReflected on itself directly
91 // Clone implements zapcore.Encoder
92 func (k *KubeAwareEncoder) Clone() zapcore.Encoder {
93 return &KubeAwareEncoder{
94 Encoder: k.Encoder.Clone(),
98 // EncodeEntry implements zapcore.Encoder
99 func (k *KubeAwareEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
101 // Kubernetes objects implement fmt.Stringer, so if we
102 // want verbose output, just delegate to that.
103 return k.Encoder.EncodeEntry(entry, fields)
106 for i, field := range fields {
107 // intercept stringer fields that happen to be Kubernetes runtime.Object or
108 // types.NamespacedName values (Kubernetes runtime.Objects commonly
109 // implement String, apparently).
110 if field.Type == zapcore.StringerType {
111 switch val := field.Interface.(type) {
113 fields[i] = zapcore.Field{
114 Type: zapcore.ObjectMarshalerType,
116 Interface: kubeObjectWrapper{obj: val},
118 case types.NamespacedName:
119 fields[i] = zapcore.Field{
120 Type: zapcore.ObjectMarshalerType,
122 Interface: namespacedNameWrapper{NamespacedName: val},
128 return k.Encoder.EncodeEntry(entry, fields)