You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Previously, error formatting logic was based on a single linear chain
of causality. Error causes would be printed vertically down the page,
and their interpretation was natural.
This commit adds multi-cause formatting support with two goals in
mind:
1. Preserve output exactly as before for single-cause error chains
2. Format multi-errors in a way that preserves the existing vertical
layout style.
For non-verbose error display (`.Error()`, `%s`, `%v`) there are no
changes implemented here. We rely on the error's own display logic and
typically a multi-cause error will have a message within that has been
constructed from all of its causes during instantiation.
For verbose error display (`%+v`) which relies on object
introspection, whenever we encounter a multi-cause error in the chain,
we mark that subtree as being displayed with markers for the "depth"
of various causes. All child errors of the parent, will be at depth
"1", their child errors will be at depth "2", etc. During display, we
indent the error by its "depth" and add a `└─` symbol to illustrate
the parent/child relationship.
Example:
Printing the error produced by this construction using the format directive `%+v`
```
fmt.Errorf(
"prefix1: %w",
fmt.Errorf(
"prefix2 %w",
goErr.Join(
fmt.Errorf("a%w", fmt.Errorf("b%w", fmt.Errorf("c%w", goErr.New("d")))),
fmt.Errorf("e%w", fmt.Errorf("f%w", fmt.Errorf("g%w", goErr.New("h")))),
)))
```
Produces the following output:
```
prefix1: prefix2: abcd
(1) prefix1
Wraps: (2) prefix2
Wraps: (3) abcd
| efgh
└─ Wraps: (4) efgh
└─ Wraps: (5) fgh
└─ Wraps: (6) gh
└─ Wraps: (7) h
└─ Wraps: (8) abcd
└─ Wraps: (9) bcd
└─ Wraps: (10) cd
└─ Wraps: (11) d
Error types: (1) *fmt.wrapError (2) *fmt.wrapError (3)
*errors.joinError (4) *fmt.wrapError (5) *fmt.wrapError (6)
*fmt.wrapError (7) *errors.errorString (8) *fmt.wrapError (9)
*fmt.wrapError (10) *fmt.wrapError (11) *errors.errorString`,
```
Note the following properties of the output:
* The top-level single cause chain maintains the left-aligned `Wrap`
lines which contain each cause one at a time.
* As soon as we hit the multi-cause errors within the `joinError`
struct (`(3)`), we add new indentation to show that The child errors
of `(3)` are `(4)` and `(8)`
* Subsequent causes of errors after `joinError`, are also indented to
disambiguate the causal chain
* The `Error types` section at the bottom remains the same and the
numbering of the types can be matched to the errors above using the
integers next to the `Wrap` lines.
* No special effort has been made to number the errors in a way that
describes the causal chain or tree. This keeps it easy to match up the
error to its type descriptor.
0 commit comments