WIP: structured error, funcr.PseudoStruct#361
Conversation
|
/assign @harshanarayana |
|
The Kubernetes project currently lacks enough contributors to adequately respond to all PRs. This bot triages PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /lifecycle stale |
|
The Kubernetes project currently lacks enough active contributors to adequately respond to all PRs. This bot triages PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /lifecycle rotten |
|
/remove-lifecycle rotten Waiting for slog in Go 1.21. |
|
We could do this now, but I'd like to hear from @tallclair first whether this feature is really needed and going to be used. |
|
The Kubernetes project currently lacks enough contributors to adequately respond to all PRs. This bot triages PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /lifecycle stale |
|
The Kubernetes project currently lacks enough active contributors to adequately respond to all PRs. This bot triages PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /lifecycle rotten |
|
The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. This bot triages PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /close |
|
@k8s-triage-robot: Closed this PR. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
This special type can be used as value in WithValues/Info/Error or as result of MarshalLog. It gets rendered like a struct, with curly brackets around the content. While at it, the import aliases get updated: - link to docs - add PseudoStruct (new) - add Marshaler (an omission that currently forces several Kubernetes files to reference logr directly)
|
/reopen Still relevant for #357 |
|
@pohly: Reopened this PR. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: pohly The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
2ca1701 to
57e1146
Compare
57e1146 to
7df2671
Compare
|
/remove-lifecycle rotten Updated based on the discussion in go-logr/zapr#56. /hold The zapr PR should get merged first, then used in this PR for the examples, which will depend on updating /assign @tallclair You asked for this in #357. |
| // Other backends might not support this, so all relevant information | ||
| // should be in the error string. | ||
| type ErrorDetailer interface { | ||
| ErrorDetails() any |
There was a problem hiding this comment.
This could become a type alias for logr.ErrorDetailer. It probably makes sense to decide whether we want such a type there before merging this and the zapr PR.
I'll prepare a logr PR for discussion.
/cc @thockin
| {"caller":"test/output.go:<LINE>","msg":"duplicates","trace":"101112131415161718191a1b1c1d1e1f","span":"0102030405060708","a":1,"v":0} | ||
| {"caller":"test/output.go:<LINE>","msg":"duplicates","trace":"101112131415161718191a1b1c1d1e1f","span":"0102030405060708","a":1,"c":3,"trace":"101112131415161718191a1b1c1d1e1f","span":"2122232425262728","d":4,"v":0} | ||
| `, | ||
| // Without support for funcr.PseudoStruct, zapr falls back to rendering a list. |
There was a problem hiding this comment.
With go-logr/zapr#56, zapr works as desired:
diff --git a/test/zapr.go b/test/zapr.go
index be5de7e..9b5d71c 100644
--- a/test/zapr.go
+++ b/test/zapr.go
@@ -283,36 +283,34 @@ I output.go:<LINE>] "duplicates" trace="101112131415161718191a1b1c1d1e1f" a=1 c=
{"caller":"test/output.go:<LINE>","msg":"duplicates","trace":"101112131415161718191a1b1c1d1e1f","span":"0102030405060708","a":1,"v":0}
{"caller":"test/output.go:<LINE>","msg":"duplicates","trace":"101112131415161718191a1b1c1d1e1f","span":"0102030405060708","a":1,"c":3,"trace":"101112131415161718191a1b1c1d1e1f","span":"2122232425262728","d":4,"v":0}
`,
- // Without support for funcr.PseudoStruct, zapr falls back to rendering a list.
`I output.go:<LINE>] "keys and values" parent={ boolsub=true intsub=1 recursive={ sub="level2" } multiLine=<
abc
def
> }
-`: `{"caller":"test/output.go:<LINE>","msg":"keys and values","v":0,"parent":["boolsub",true,"intsub",1,"recursive",["sub","level2"],"multiLine","abc\ndef"]}
+`: `{"caller":"test/output.go:<LINE>","msg":"keys and values","v":0,"parent":{"boolsub":true,"intsub":1,"recursive":{"sub":"level2"},"multiLine":"abc\ndef"}}
`,
- // Errors are rendered without details by zapr.
- // This is okay, they were meant to be optional.
+ // Errors are rendered with details by zapr, including support for PseudoStruct.
`I output.go:<LINE>] "structured error" someErr="fake error" someErrDetails={ x=1 y=<
multi-line
string
> }
-`: `{"caller":"test/output.go:<LINE>","msg":"structured error","v":0,"someErr":"fake error"}
+`: `{"caller":"test/output.go:<LINE>","msg":"structured error","v":0,"someErr":"fake error","someErrDetails":{"x":1,"y":"multi-line\nstring"}}
`,
`I output.go:<LINE>] "my structured error" someErr="fake error" someErrDetails={"SomeInt":1,"SomeString":"multi-line\nstring"}
-`: `{"caller":"test/output.go:<LINE>","msg":"my structured error","v":0,"someErr":"fake error"}
+`: `{"caller":"test/output.go:<LINE>","msg":"my structured error","v":0,"someErr":"fake error","someErrDetails":{"SomeInt":1,"SomeString":"multi-line\nstring"}}
`,
`E output.go:<LINE>] "structured error" err="fake error" errDetails={ x=1 y=<
multi-line
string
> }
-`: `{"caller":"test/output.go:<LINE>","msg":"structured error","err":"fake error"}
+`: `{"caller":"test/output.go:<LINE>","msg":"structured error","err":"fake error","errDetails":{"x":1,"y":"multi-line\nstring"}}
`,
`E output.go:<LINE>] "my structured error" err="fake error" errDetails={"SomeInt":1,"SomeString":"multi-line\nstring"}
-`: `{"caller":"test/output.go:<LINE>","msg":"my structured error","err":"fake error"}
+`: `{"caller":"test/output.go:<LINE>","msg":"my structured error","err":"fake error","errDetails":{"SomeInt":1,"SomeString":"multi-line\nstring"}}
`,
}
}| def | ||
| > } | ||
| `, | ||
| }, |
There was a problem hiding this comment.
TODO:
- ErrorDetails implementation which crashes (should be caught by backends).
7df2671 to
f7651cc
Compare
A "structured error" is an error type that implements both Error and ErrorDetails. It gets logged like a normal error with "err=<Error()>", but then another "errDetails=<ErrorDetails()>" gets added to log additional information that may be stored in the error. The result of ErrorDetails is logged like any other value. Beware that rendering of structs, in particular multi-line strings in structs, is not very readable because it all gets handled by json.Marshal. This also means that unexported fields are not logged. To get nicer output of multiple values, a PseudoStruct can be returned. Those values then are formatted one-by-one by klog, which means that multi-line strings are readable.
What this PR does / why we need it:
Nicer formatting of error details.
Which issue(s) this PR fixes:
Fixes #357
Special notes for your reviewer:
This is a variant of the idea with the existing funcr.PseudoType.
Release note: