diff --git a/internal/serialize/keyvalues.go b/internal/serialize/keyvalues.go index 0bff64d0..f9558c3d 100644 --- a/internal/serialize/keyvalues.go +++ b/internal/serialize/keyvalues.go @@ -24,6 +24,10 @@ import ( "github.com/go-logr/logr" ) +type textWriter interface { + WriteText(*bytes.Buffer) +} + // WithValues implements LogSink.WithValues. The old key/value pairs are // assumed to be well-formed, the new ones are checked and padded if // necessary. It returns a new slice. @@ -175,6 +179,8 @@ func KVFormat(b *bytes.Buffer, k, v interface{}) { // than plain strings // (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235). switch v := v.(type) { + case textWriter: + writeTextWriterValue(b, v) case fmt.Stringer: writeStringValue(b, true, StringerToString(v)) case string: @@ -254,6 +260,16 @@ func ErrorToString(err error) (ret string) { return } +func writeTextWriterValue(b *bytes.Buffer, v textWriter) { + b.WriteRune('=') + defer func() { + if err := recover(); err != nil { + fmt.Fprintf(b, `""`, err) + } + }() + v.WriteText(b) +} + func writeStringValue(b *bytes.Buffer, quote bool, v string) { data := []byte(v) index := bytes.IndexByte(data, '\n') diff --git a/k8s_references.go b/k8s_references.go index a6436b5d..ecd3f8b6 100644 --- a/k8s_references.go +++ b/k8s_references.go @@ -17,6 +17,7 @@ limitations under the License. package klog import ( + "bytes" "fmt" "reflect" "strings" @@ -42,6 +43,20 @@ func (ref ObjectRef) String() string { return ref.Name } +func (ref ObjectRef) WriteText(out *bytes.Buffer) { + out.WriteRune('"') + ref.writeUnquoted(out) + out.WriteRune('"') +} + +func (ref ObjectRef) writeUnquoted(out *bytes.Buffer) { + if ref.Namespace != "" { + out.WriteString(ref.Namespace) + out.WriteRune('/') + } + out.WriteString(ref.Name) +} + // MarshalLog ensures that loggers with support for structured output will log // as a struct by removing the String method via a custom type. func (ref ObjectRef) MarshalLog() interface{} { @@ -123,31 +138,31 @@ var _ fmt.Stringer = kobjSlice{} var _ logr.Marshaler = kobjSlice{} func (ks kobjSlice) String() string { - objectRefs, err := ks.process() - if err != nil { - return err.Error() + objectRefs, errStr := ks.process() + if errStr != "" { + return errStr } return fmt.Sprintf("%v", objectRefs) } func (ks kobjSlice) MarshalLog() interface{} { - objectRefs, err := ks.process() - if err != nil { - return err.Error() + objectRefs, errStr := ks.process() + if errStr != "" { + return errStr } return objectRefs } -func (ks kobjSlice) process() ([]interface{}, error) { +func (ks kobjSlice) process() (objs []interface{}, err string) { s := reflect.ValueOf(ks.arg) switch s.Kind() { case reflect.Invalid: // nil parameter, print as nil. - return nil, nil + return nil, "" case reflect.Slice: // Okay, handle below. default: - return nil, fmt.Errorf("", ks.arg) + return nil, fmt.Sprintf("", ks.arg) } objectRefs := make([]interface{}, 0, s.Len()) for i := 0; i < s.Len(); i++ { @@ -157,8 +172,41 @@ func (ks kobjSlice) process() ([]interface{}, error) { } else if v, ok := item.(KMetadata); ok { objectRefs = append(objectRefs, KObj(v)) } else { - return nil, fmt.Errorf("", item) + return nil, fmt.Sprintf("", item) + } + } + return objectRefs, "" +} + +var nilToken = []byte("") + +func (ks kobjSlice) WriteText(out *bytes.Buffer) { + s := reflect.ValueOf(ks.arg) + switch s.Kind() { + case reflect.Invalid: + // nil parameter, print as empty slice. + out.WriteString("[]") + return + case reflect.Slice: + // Okay, handle below. + default: + fmt.Fprintf(out, `""`, ks.arg) + return + } + out.Write([]byte{'['}) + defer out.Write([]byte{']'}) + for i := 0; i < s.Len(); i++ { + if i > 0 { + out.Write([]byte{' '}) + } + item := s.Index(i).Interface() + if item == nil { + out.Write(nilToken) + } else if v, ok := item.(KMetadata); ok { + KObj(v).writeUnquoted(out) + } else { + fmt.Fprintf(out, "", item) + return } } - return objectRefs, nil } diff --git a/test/output.go b/test/output.go index bfd026c2..4dc52f76 100644 --- a/test/output.go +++ b/test/output.go @@ -290,14 +290,14 @@ I output.go:] "test" firstKey=1 secondKey=3 &kmeta{Name: "pod-1", Namespace: "kube-system"}, &kmeta{Name: "pod-2", Namespace: "kube-system"}, })}, - expectedOutput: `I output.go:] "test" pods="[kube-system/pod-1 kube-system/pod-2]" + expectedOutput: `I output.go:] "test" pods=[kube-system/pod-1 kube-system/pod-2] `, }, "KObjSlice nil arg": { text: "test", values: []interface{}{"pods", klog.KObjSlice(nil)}, - expectedOutput: `I output.go:] "test" pods="[]" + expectedOutput: `I output.go:] "test" pods=[] `, }, "KObjSlice int arg": { @@ -314,14 +314,14 @@ I output.go:] "test" firstKey=1 secondKey=3 &kmeta{Name: "pod-1", Namespace: "kube-system"}, nil, })}, - expectedOutput: `I output.go:] "test" pods="[kube-system/pod-1 ]" + expectedOutput: `I output.go:] "test" pods=[kube-system/pod-1 ] `, }, "KObjSlice ints": { text: "test", values: []interface{}{"ints", klog.KObjSlice([]int{1, 2, 3})}, - expectedOutput: `I output.go:] "test" ints="" + expectedOutput: `I output.go:] "test" ints=[] `, }, "regular error types as value": { @@ -357,7 +357,7 @@ I output.go:] "test" firstKey=1 secondKey=3 "MarshalLog() for nil": { text: "marshaler nil", values: []interface{}{"obj", (*klog.ObjectRef)(nil)}, - expectedOutput: `I output.go:] "marshaler nil" obj="" + expectedOutput: `I output.go:] "marshaler nil" obj="" `, }, "Error() that panics": { diff --git a/test/zapr.go b/test/zapr.go index 48cf2c3d..680e022d 100644 --- a/test/zapr.go +++ b/test/zapr.go @@ -73,11 +73,7 @@ func ZaprOutputMappingDirect() map[string]string { `: `{"caller":"test/output.go:","msg":"test","v":0,"pods":[{"name":"pod-1","namespace":"kube-system"},{"name":"pod-2","namespace":"kube-system"}]} `, - `I output.go:] "test" pods="[kube-system/pod-1 kube-system/pod-2]" -`: `{"caller":"test/output.go:","msg":"test","v":0,"pods":[{"name":"pod-1","namespace":"kube-system"},{"name":"pod-2","namespace":"kube-system"}]} -`, - - `I output.go:] "test" pods="[]" + `I output.go:] "test" pods=[] `: `{"caller":"test/output.go:","msg":"test","v":0,"pods":null} `, @@ -85,11 +81,11 @@ func ZaprOutputMappingDirect() map[string]string { `: `{"caller":"test/output.go:","msg":"test","v":0,"pods":""} `, - `I output.go:] "test" ints="" + `I output.go:] "test" ints=[] `: `{"caller":"test/output.go:","msg":"test","v":0,"ints":""} `, - `I output.go:] "test" pods="[kube-system/pod-1 ]" + `I output.go:] "test" pods=[kube-system/pod-1 ] `: `{"caller":"test/output.go:","msg":"test","v":0,"pods":[{"name":"pod-1","namespace":"kube-system"},null]} `, @@ -140,7 +136,7 @@ I output.go:] "odd WithValues" keyWithoutValue="(MISSING)" {"caller":"test/output.go:","msg":"both odd","basekey1":"basevar1","v":0,"akey":"avalue"} `, - `I output.go:] "marshaler nil" obj="" + `I output.go:] "marshaler nil" obj="" `: `{"caller":"test/output.go:","msg":"marshaler nil","v":0,"objError":"PANIC=value method k8s.io/klog/v2.ObjectRef.MarshalLog called using nil *ObjectRef pointer"} `,