Skip to content

Commit

Permalink
support logr.Marshaler
Browse files Browse the repository at this point in the history
This is useful for proxy objects that want the actual value to be logged as
something other than a string.
  • Loading branch information
pohly committed Apr 27, 2022
1 parent d029fb7 commit a0ed0fd
Showing 1 changed file with 46 additions and 29 deletions.
75 changes: 46 additions & 29 deletions internal/serialize/keyvalues.go
Expand Up @@ -20,6 +20,8 @@ import (
"bytes"
"fmt"
"strconv"

"github.com/go-logr/logr"
)

// WithValues implements LogSink.WithValues. The old key/value pairs are
Expand Down Expand Up @@ -118,36 +120,41 @@ func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
} else {
b.WriteString(fmt.Sprintf("%s", k))
}
writeValue(b, v)
}
}

// The type checks are sorted so that more frequently used ones
// come first because that is then faster in the common
// cases. In Kubernetes, ObjectRef (a Stringer) is more common
// than plain strings
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
switch v := v.(type) {
case fmt.Stringer:
writeStringValue(b, true, StringerToString(v))
case string:
writeStringValue(b, true, v)
case error:
writeStringValue(b, true, ErrorToString(v))
case []byte:
// In https://github.com/kubernetes/klog/pull/237 it was decided
// to format byte slices with "%+q". The advantages of that are:
// - readable output if the bytes happen to be printable
// - non-printable bytes get represented as unicode escape
// sequences (\uxxxx)
//
// The downsides are that we cannot use the faster
// strconv.Quote here and that multi-line output is not
// supported. If developers know that a byte array is
// printable and they want multi-line output, they can
// convert the value to string before logging it.
b.WriteByte('=')
b.WriteString(fmt.Sprintf("%+q", v))
default:
writeStringValue(b, false, fmt.Sprintf("%+v", v))
}
func writeValue(b *bytes.Buffer, v interface{}) {
// The type checks are sorted so that more frequently used ones
// come first because that is then faster in the common
// cases. In Kubernetes, ObjectRef (a Stringer) is more common
// than plain strings
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
switch v := v.(type) {
case fmt.Stringer:
writeStringValue(b, true, StringerToString(v))
case string:
writeStringValue(b, true, v)
case error:
writeStringValue(b, true, ErrorToString(v))
case logr.Marshaler:
writeMarshaler(b, v)
case []byte:
// In https://github.com/kubernetes/klog/pull/237 it was decided
// to format byte slices with "%+q". The advantages of that are:
// - readable output if the bytes happen to be printable
// - non-printable bytes get represented as unicode escape
// sequences (\uxxxx)
//
// The downsides are that we cannot use the faster
// strconv.Quote here and that multi-line output is not
// supported. If developers know that a byte array is
// printable and they want multi-line output, they can
// convert the value to string before logging it.
b.WriteByte('=')
b.WriteString(fmt.Sprintf("%+q", v))
default:
writeStringValue(b, false, fmt.Sprintf("%+v", v))
}
}

Expand Down Expand Up @@ -175,6 +182,16 @@ func ErrorToString(err error) (ret string) {
return
}

func writeMarshaler(b *bytes.Buffer, v logr.Marshaler) {
defer func() {
if err := recover(); err != nil {
// TODO: update tests. For now, behave as before.
writeStringValue(b, false, fmt.Sprintf("%+v", v))
}
}()
writeValue(b, v.MarshalLog())
}

func writeStringValue(b *bytes.Buffer, quote bool, v string) {
data := []byte(v)
index := bytes.IndexByte(data, '\n')
Expand Down

0 comments on commit a0ed0fd

Please sign in to comment.