-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
funcr: Handle nil Stringer, Marshaler, error #130
Conversation
funcr/funcr.go
Outdated
@@ -341,6 +341,17 @@ const ( | |||
flagRawStruct = 0x1 // do not print braces on structs | |||
) | |||
|
|||
func isNil(i interface{}) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this faster than wrapping the MarshalLog and String invocations in a function which recovers from a failure inside the called function?
That's how Dims solved it in klog:
kubernetes/klog@125dec9#diff-d11e021bcd662e6127823cd93eeac7a9aec303039137c2f27974cff399142e84
It has two advantages:
- if the interface contains a valid pointer to a struct but the called function then crashes for some other reason (nil access for a field in that struct, for example), then the recovery mechanism catches that, too
- if the function implementation itself handles nil gracefully, then logging keeps using that fallback instead of making up its own
It has a distinct disadvantage, too - if the user code panics for ANY
REASON, we'll just eat it and continue. Bad.
The point about valid implementation is apt. Looking at how printf does
it, I guess that is the better answer..
…On Thu, Jan 20, 2022 at 12:17 AM Patrick Ohly ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In funcr/funcr.go
<#130 (comment)>:
> @@ -341,6 +341,17 @@ const (
flagRawStruct = 0x1 // do not print braces on structs
)
+func isNil(i interface{}) bool {
Is this faster than wrapping the MarshalLog and String invocations in a
function which recovers from a failure inside the called function?
That's how Dims solved it in klog:
***@***.***
#diff-d11e021bcd662e6127823cd93eeac7a9aec303039137c2f27974cff399142e84
<kubernetes/klog@125dec9#diff-d11e021bcd662e6127823cd93eeac7a9aec303039137c2f27974cff399142e84>
It has two advantages:
- if the interface contains a valid pointer to a struct but the called
function then crashes for some other reason (nil access for a field in that
struct, for example), then the recovery mechanism catches that, too
- if the function implementation itself handles nil gracefully, then
logging keeps using that fallback instead of making up its own
—
Reply to this email directly, view it on GitHub
<#130 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABKWAVF23T24C6K6NUJOBT3UW7AH5ANCNFSM5MLC2XFQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Retooled - PTAL |
We could log the panic as an error - that might raise awareness that the code is not as robust as it needs to be. Hiding the problem where it won't be noticed isn't good, but I think it's still better than aborting the program. Log calls are often not as well tested as the rest of the code. Suppose someone bumps up the log level to debug some problem and then the program aborts somewhere else entirely because of a nil pointer exception - they might not be able to fix the problem and are stuck.
It dumps the panic: https://go.dev/play/p/KC2odl1qSZf |
funcr/funcr.go
Outdated
if isNil(m) { | ||
ret = "<nil-logr-marshaler>" | ||
} else { | ||
panic(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should return a string explaining the problem, similar to what fmt.Sprint does.
We could also log an error, to raise awareness.
I don't know. I sort of feel like, if the user called `panic()` and we
swallow their panic, we're doing something wrong. I mean, really this is a
very corner-case of a corner-case, but I thought passing the panic() on was
less surprising.
https://go.dev/play/p/NGMzpwFf90I
That's just weird to me
…On Thu, Jan 20, 2022 at 11:43 PM Patrick Ohly ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In funcr/funcr.go
<#130 (comment)>:
> @@ -596,6 +596,56 @@ func isEmpty(v reflect.Value) bool {
return false
}
+func invokeMarshaler(m logr.Marshaler) (ret interface{}) {
+ defer func() {
+ if err := recover(); err != nil {
+ if isNil(m) {
+ ret = "<nil-logr-marshaler>"
+ } else {
+ panic(err)
I think this should return a string explaining the problem, similar to
what fmt.Sprint does.
We could also log an error, to raise awareness.
—
Reply to this email directly, view it on GitHub
<#130 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABKWAVAQ4YWU77CKWCCC6XLUXEFA7ANCNFSM5MLC2XFQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Tim Hockin ***@***.***> writes:
I don't know. I sort of feel like, if the user called `panic()` and we
swallow their panic, we're doing something wrong.
I guess the more common case will be that user didn't explicitly call
panic, but rather forgot a nil for a field or a bounds check. We may be
able to tell the difference from looking at the error from recover, but
I am not sure whether we should bother.
|
Alt model pushed as a new commit, but I am still unsure what is "right".
DO NOT MERGE YET PLEASE :)
…On Fri, Jan 21, 2022 at 5:23 AM Patrick Ohly ***@***.***> wrote:
Tim Hockin ***@***.***> writes:
> I don't know. I sort of feel like, if the user called `panic()` and we
> swallow their panic, we're doing something wrong.
I guess the more common case will be that user didn't explicitly call
panic, but rather forgot a nil for a field or a bounds check. We may be
able to tell the difference from looking at the error from recover, but
I am not sure whether we should bother.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
I put up a Twitter poll and it was almost evenly split between "let the panic happen" and "catch panics and print", with "let them happen" taking a single-digit percentage lead. Thoughts? |
I prefer to emulate the behavior of fmt. Besides being consistent (why should |
Produce a maybe-useful error rather than panic when we are in the middle of logging a value (similar to fmt.Printf).
OK, I retooled to be similar to fmt. Added it to benchmark, but nothing to report. PTAL |
The function might panic. The conclusion in go-logr/logr#130 was that the logger should record that. zapr only needs to do that when it calls MarshalLog. Strings and errors are handled by zap. For the sake of simplicity no attempt is made to detect the reason for the panic. As zapr cannot replace the key, it uses the same replacement value as funcr because that stands out a bit more compared to the value used by zap.
The previous fix only covered fmt.Stringer.String in klog, but not klogr. error.Error and logr.Marshaler.MarshalLog have the same problem. The replacement string now captures the error, which makes it consistent with go-logr/logr#130.
The function might panic. The conclusion in go-logr/logr#130 was that the logger should record that. zapr only needs to do that when it calls MarshalLog. Strings and errors are handled by zap. For the sake of simplicity no attempt is made to detect the reason for the panic. In case of a panic, the key is replaced by "<key>Error" and the value with "PANIC=<panic reason>". This is consistent with how zap handles panics.
The function might panic. The conclusion in go-logr/logr#130 was that the logger should record that. zapr only needs to do that when it calls MarshalLog. Strings and errors are handled by zap. For the sake of simplicity no attempt is made to detect the reason for the panic. In case of a panic, the key is replaced by "<key>Error" and the value with "PANIC=<panic reason>". This is consistent with how zap handles panics.
The previous fix only covered fmt.Stringer.String in klog, but not klogr. error.Error and logr.Marshaler.MarshalLog have the same problem. The replacement string now captures the error, which makes it consistent with go-logr/logr#130. Two different corner cases may be handled differently: - panic for a nil object - panic for a valid object Only zapr v1.2.3 handles the panic in MarshalLog.
Produce a useful error rather than panic.
Fixes #125