Skip to content

Commit

Permalink
Merge pull request #299 from pohly/handler-panics-in-error
Browse files Browse the repository at this point in the history
handle panics in MarshalLog, Error, String
  • Loading branch information
k8s-ci-robot committed Feb 21, 2022
2 parents 84f3ebd + 708ae61 commit e0de690
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 7 deletions.
2 changes: 1 addition & 1 deletion examples/go.mod
Expand Up @@ -4,7 +4,7 @@ go 1.13

require (
github.com/go-logr/logr v1.2.2
github.com/go-logr/zapr v1.2.2
github.com/go-logr/zapr v1.2.3
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
go.uber.org/zap v1.19.0
k8s.io/klog/v2 v2.30.0
Expand Down
2 changes: 2 additions & 0 deletions examples/go.sum
Expand Up @@ -8,6 +8,8 @@ github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/zapr v1.2.2 h1:5YNlIL6oZLydaV4dOFjL8YpgXF/tPeTbnpatnu3cq6o=
github.com/go-logr/zapr v1.2.2/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
Expand Down
22 changes: 18 additions & 4 deletions internal/serialize/keyvalues.go
Expand Up @@ -126,11 +126,11 @@ func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
switch v := v.(type) {
case fmt.Stringer:
writeStringValue(b, true, stringerToString(v))
writeStringValue(b, true, StringerToString(v))
case string:
writeStringValue(b, true, v)
case error:
writeStringValue(b, true, v.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:
Expand All @@ -151,16 +151,30 @@ func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
}
}

func stringerToString(s fmt.Stringer) (ret string) {
// StringerToString converts a Stringer to a string,
// handling panics if they occur.
func StringerToString(s fmt.Stringer) (ret string) {
defer func() {
if err := recover(); err != nil {
ret = "nil"
ret = fmt.Sprintf("<panic: %s>", err)
}
}()
ret = s.String()
return
}

// ErrorToString converts an error to a string,
// handling panics if they occur.
func ErrorToString(err error) (ret string) {
defer func() {
if err := recover(); err != nil {
ret = fmt.Sprintf("<panic: %s>", err)
}
}()
ret = err.Error()
return
}

func writeStringValue(b *bytes.Buffer, quote bool, v string) {
data := []byte(v)
index := bytes.IndexByte(data, '\n')
Expand Down
2 changes: 1 addition & 1 deletion internal/serialize/keyvalues_test.go
Expand Up @@ -137,7 +137,7 @@ No whitespace.`,
},
{
keysValues: []interface{}{"point-1", point{100, 200}, "point-2", emptyPoint},
want: " point-1=\"x=100, y=200\" point-2=\"nil\"",
want: " point-1=\"x=100, y=200\" point-2=\"<panic: value method k8s.io/klog/v2/internal/serialize_test.point.String called using nil *point pointer>\"",
},
}

Expand Down
2 changes: 1 addition & 1 deletion klogr/klogr.go
Expand Up @@ -140,7 +140,7 @@ func (l klogger) Error(err error, msg string, kvList ...interface{}) {
msgStr := flatten("msg", msg)
var loggableErr interface{}
if err != nil {
loggableErr = err.Error()
loggableErr = serialize.ErrorToString(err)
}
switch l.format {
case FormatSerialize:
Expand Down
74 changes: 74 additions & 0 deletions test/output.go
Expand Up @@ -286,6 +286,42 @@ I output.go:<LINE>] "test" firstKey=1 secondKey=3
text: "test",
err: errors.New("whoops"),
expectedOutput: `E output.go:<LINE>] "test" err="whoops"
`,
},
"Error() for nil": {
text: "error nil",
err: (*customErrorJSON)(nil),
expectedOutput: `E output.go:<LINE>] "error nil" err="<panic: runtime error: invalid memory address or nil pointer dereference>"
`,
},
"String() for nil": {
text: "stringer nil",
values: []interface{}{"stringer", (*stringer)(nil)},
expectedOutput: `I output.go:<LINE>] "stringer nil" stringer="<panic: runtime error: invalid memory address or nil pointer dereference>"
`,
},
"MarshalLog() for nil": {
text: "marshaler nil",
values: []interface{}{"obj", (*klog.ObjectRef)(nil)},
expectedOutput: `I output.go:<LINE>] "marshaler nil" obj="<panic: value method k8s.io/klog/v2.ObjectRef.String called using nil *ObjectRef pointer>"
`,
},
"Error() that panics": {
text: "error panic",
err: faultyError{},
expectedOutput: `E output.go:<LINE>] "error panic" err="<panic: fake Error panic>"
`,
},
"String() that panics": {
text: "stringer panic",
values: []interface{}{"stringer", faultyStringer{}},
expectedOutput: `I output.go:<LINE>] "stringer panic" stringer="<panic: fake String panic>"
`,
},
"MarshalLog() that panics": {
text: "marshaler panic",
values: []interface{}{"obj", faultyMarshaler{}},
expectedOutput: `I output.go:<LINE>] "marshaler panic" obj={}
`,
},
}
Expand Down Expand Up @@ -699,3 +735,41 @@ func (e *customErrorJSON) Error() string {
func (e *customErrorJSON) MarshalJSON() ([]byte, error) {
return json.Marshal(strings.ToUpper(e.s))
}

type stringer struct {
s string
}

// String crashes when called for nil.
func (s *stringer) String() string {
return s.s
}

var _ fmt.Stringer = &stringer{}

type faultyStringer struct{}

// String always panics.
func (f faultyStringer) String() string {
panic("fake String panic")
}

var _ fmt.Stringer = faultyStringer{}

type faultyMarshaler struct{}

// MarshalLog always panics.
func (f faultyMarshaler) MarshalLog() interface{} {
panic("fake MarshalLog panic")
}

var _ logr.Marshaler = faultyMarshaler{}

type faultyError struct{}

// Error always panics.
func (f faultyError) Error() string {
panic("fake Error panic")
}

var _ error = faultyError{}
25 changes: 25 additions & 0 deletions test/zapr.go
Expand Up @@ -114,6 +114,31 @@ I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)"
`: `{"caller":"test/output.go:<WITH-VALUES>","msg":"odd number of arguments passed as key-value pairs for logging","ignored key":"basekey2"}
{"caller":"test/output.go:<LINE>","msg":"odd number of arguments passed as key-value pairs for logging","basekey1":"basevar1","ignored key":"akey2"}
{"caller":"test/output.go:<LINE>","msg":"both odd","basekey1":"basevar1","v":0,"akey":"avalue"}
`,

`I output.go:<LINE>] "marshaler nil" obj="<panic: value method k8s.io/klog/v2.ObjectRef.String called using nil *ObjectRef pointer>"
`: `{"caller":"test/output.go:<LINE>","msg":"marshaler nil","v":0,"objError":"PANIC=value method k8s.io/klog/v2.ObjectRef.MarshalLog called using nil *ObjectRef pointer"}
`,

// zap replaces a panic for a nil object with <nil>.
`E output.go:<LINE>] "error nil" err="<panic: runtime error: invalid memory address or nil pointer dereference>"
`: `{"caller":"test/output.go:<LINE>","msg":"error nil","err":"<nil>"}
`,

`I output.go:<LINE>] "stringer nil" stringer="<panic: runtime error: invalid memory address or nil pointer dereference>"
`: `{"caller":"test/output.go:<LINE>","msg":"stringer nil","v":0,"stringer":"<nil>"}
`,

`I output.go:<LINE>] "stringer panic" stringer="<panic: fake String panic>"
`: `{"caller":"test/output.go:<LINE>","msg":"stringer panic","v":0,"stringerError":"PANIC=fake String panic"}
`,

`E output.go:<LINE>] "error panic" err="<panic: fake Error panic>"
`: `{"caller":"test/output.go:<LINE>","msg":"error panic","errError":"PANIC=fake Error panic"}
`,

`I output.go:<LINE>] "marshaler panic" obj={}
`: `{"caller":"test/output.go:<LINE>","msg":"marshaler panic","v":0,"objError":"PANIC=fake MarshalLog panic"}
`,

// klog.Info
Expand Down

0 comments on commit e0de690

Please sign in to comment.