Skip to content

Conversation

@pohly
Copy link
Contributor

@pohly pohly commented Nov 23, 2022

This is based on #60 and adds support for "structured errors" (see kubernetes/klog#357).

@pohly pohly changed the title RFC: special types: structured error and logr.KeysAndValues RFC: special types: structured error Sep 19, 2023
@pohly pohly force-pushed the special-types branch 2 times, most recently from ed3114f to 1809a60 Compare November 21, 2023 13:30
Copy link
Contributor

@thockin thockin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know zap well, but I am fine with this, I think. A few questions.

return zap.Skip()
}
return zap.Inline(zapcore.ObjectMarshalerFunc(func(encoder zapcore.ObjectEncoder) error {
// Always log as a normal error first.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this logs both? If someone implemented LogValuer, I would expect ONLY that, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it did that under the key for the structured error value, then we would end up with "err" changing it's content from string (normal errors) to struct (structured error). We decided against that in kubernetes/klog#357 (comment).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the PR with documentation.

As we now agree on this approach I'll make the same change in the klog text logger. I'm undecided whether this should be documented in the logr docs. It's an implementation detail of some logging backends, but the more backends agree on this as a convention, the better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/hold

  • extend textlogger
  • extend klog with a helper which constructs a structured error (e.g. ErrWithDetails(err error, details any) error - need to think the details (sic!) and what type they should have).

}

type structuredError struct {
error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from an obviousness POV, I would just write this as an empty struct, with an Error() method and an LogValue() method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

A "structured error" is an error which implements error and slog.LogValuer.
When encountering such an error, it gets logged normally and then another field
gets added with the result of LogValue().

The name of the extra field is the original field name plus a configurable
suffix, with "Details" as default.

The extra details are logged as if they had been passed as a value to
zapr. slog.Value.Resolve is used to protect against errors and recursion while
calling LogValue, but does not protect against recursion that can occur when
LogValue returns the original error: then zapIt->zapError->zapIt->... repeats
until the program gets killed.

A simple guard against this (not expanding error again while formatting an
error) is too simplistic and would prevent nice rendering of a wrapped error
that might get returned by MarshalLog. zapIt would have to track the exact
error instance and detect when it gets the same value again.
Copy link
Contributor Author

@pohly pohly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return zap.Skip()
}
return zap.Inline(zapcore.ObjectMarshalerFunc(func(encoder zapcore.ObjectEncoder) error {
// Always log as a normal error first.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it did that under the key for the structured error value, then we would end up with "err" changing it's content from string (normal errors) to struct (structured error). We decided against that in kubernetes/klog#357 (comment).

}

type structuredError struct {
error
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

return zap.Skip()
}
return zap.Inline(zapcore.ObjectMarshalerFunc(func(encoder zapcore.ObjectEncoder) error {
// Always log as a normal error first.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the PR with documentation.

As we now agree on this approach I'll make the same change in the klog text logger. I'm undecided whether this should be documented in the logr docs. It's an implementation detail of some logging backends, but the more backends agree on this as a convention, the better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants