Documentation
¶
Overview ¶
Package errtag provides functionality to add metadata (tag+value pairs) to errors without affecting either their queryability (via errors.Is or errors.As) or their rendering.
Example:
type Flavor int const ( Bland Flavor = iota Sweet Salty Savory Sour ) // FlavorTag has the default value Bland. var FlavorTag = errtag.Make("somepkg.Flavor", Bland) func something() error { var err error ... return FlavorTag.ApplyValue(err, Savory) } func otherFunc() { err := something() if FlavorTag.ValueOrDefault(err) == Savory { ... } }
Index ¶
- type CollectedValues
- type MergeFn
- type Tag
- func (t Tag[T]) Apply(err error) (wrapped error)
- func (t Tag[T]) ApplyValue(err error, value T) (wrapped error)
- func (t Tag[T]) GenerateErrorTagValue() (key, value any)deprecated
- func (t Tag[T]) In(err error) (existsWithDefault bool)
- func (t Tag[T]) Is(other Tag[T]) bool
- func (t Tag[T]) Key() TagKey
- func (t Tag[T]) Value(err error) (value T, found bool)
- func (t Tag[T]) ValueOrDefault(err error) T
- func (t Tag[T]) WithDefault(newDefault T) Tag[T]
- type TagKey
- type TagType
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CollectedValues ¶
CollectedValues maps Tag description to one or more values for an error, obtained via Collect.
func Collect ¶
func Collect(err error, exclude ...TagType) CollectedValues
Collect scans the error for all known tag keys and returns a map of tag description to the current value for that tag.
func (CollectedValues) String ¶
func (c CollectedValues) String() string
String renders the CollectedValues to a multi-line string.
NOTE: If multiple TagKey's were created with the same description text, it is possible to see the same descriptive string on the left hand side multiple times. This probably indicates that there is a bug in the program (possibly due to copy/pasting a [MakeTag] invocation, or using MakeTag in a way where the output is not correctly reused across the process).
type MergeFn ¶
type MergeFn[T comparable] func(values []*T) *T
MergeFn is the type signature of the Merging function that Tag uses.
This function will only ever be called with len(values) > 1, and every item in values is non-nil.
It may return one of the items in `values`, or synthesize an entirely new value.
If this returns `nil`, it indicates "no value" (e.g. if your merge function allows sibling errors to 'cancel out').
This function MUST NOT modify the contents of the *T in `values`. Pointers are used here simply as a way to avoid copying many potentially large T objects. If Go provided a way to make these immutable non-pointers which weren't copied, I would do it, but unfortunately the best I can do is ask via this doc for your pinkie-promise :/.
This function MUST be left associative, i.e.
Merge(A, B, C, D)
Must be the same as:
Merge(A, Merge(B, Merge(C, D)))
type Tag ¶
type Tag[T comparable] struct { // contains filtered or unexported fields }
Tag holds everything necessary to associate values of type T with errors.
Construct this with Make.
As a compatibility feature, a Tag can currently be used with:
- errors.Annotate(err, ...).Tag(<the tag>)
- errors.New(err, <the tag>))
However, these usages are NOT recommended, and exactly equivalent to just doing `tag.Apply(err)`. At some point in the medium-term future, the errors.Annotate construct will be removed entirely and replaced with `fmt.Errorf("... %w ...", ...)`.
Tag keys ¶
Every Tag[T] has a unique description, which is enforced by Make.
Default Value ¶
Tags have a default value, which is the zero value of T, but can be changed with Tag.WithDefault.
The default value is used for Tag.Apply, Tag.In, Tag.Value and Tag.ValueOrDefault.
Merging ¶
When extracting a Value from an error, Tags do a process called 'merging'. Merging happens when an error in the stack implements `Unwrap() []error`, and multiple of those wrapped errors contain a value for Tag[T]. To provide a cohesive understanding of what the current singular value is for such an error, there needs to be some defined way to merge or pick one value.
By default Tags include a Merge function which simply picks the top-left-most value encountered with a breadth-first search, which has historically been good-enough. However some tag value types like status codes may have a concept of a 'worst' value or such. The Merge function can be set with MakeWithMerge.
func Make ¶
func Make[T comparable](description string, defaultValue T) Tag[T]
Make creates a new Tag with a default value.
This generates a new TagKey (which is a very cheap operation).
See Tag.WithDefault to change the default value.
Expected Use - Global Tags ¶
The main use for errtag is to define package-level Tag[T] instances so that packages can tag their own errors, or for multiple packages to coordinate between them for common tags, such as go.chromium.org/luci/common/retry/transient.Tag.
These can then either be exported (for multiple packages to write and/or read the tag values out of errors), or not (for just the package's own use passing data around).
This pattern is far and away the most common use of error tags.
Expected Use - Local Tags ¶
Occasionally you need to tag an error within a very narrow context, for example if you have an outer function calling something and passing a callback. In this case, the 'something' may be doing `errors.Is` checks where the callback must return errors derived (or joined) with some specific errors. It would be OK to make a tag within the outer function, and use this in the callback to pass back some data which is only directly* observable by the outer function.
Each outer function call would generate a new, unique, Tag[T].
* However, note that if the outer function returns this error, the tag value could be observed indirectly via Collect.
Merging values ¶
In the event of a 'multi error' (e.g. errors.Join, fmt.Errorf with multiple %w verbs, the LUCI errors.MultiError, etc.), when you read the tag value from an error, it may need to 'merge' multiple values for this tag.
The Tag returned by Make will merge multiple values by simply picking the first value (so - traverse the error breadth-first, picking the 'leftmost' value at the highest level encountered).
If you need to override this, use MakeWithMerge.
func MakeWithMerge ¶
func MakeWithMerge[T comparable](description string, defaultValue T, merge MergeFn[T]) Tag[T]
MakeWithMerge creates a new Tag.
This acts the same as Make, but also allows you to set a merge function.
func (Tag[T]) ApplyValue ¶
ApplyValue wraps `err` with an error containing the given value.
If `err` is nil, this returns nil.
func (Tag[T]) GenerateErrorTagValue
deprecated
GenerateErrorTagValue allows this tag to be compatible with errors.Annotate.Tag.
This is a no-op and just allows this to wrapper to be used in the errors.Annotate(...).Tag the function type signature. This should be removed when the Annotate pattern is removed from the errors package.
Deprecated: Do not use.
func (Tag[T]) In ¶
In returns true iff the Tag is present in the error AND currently has the defaultValue for the tag.
If you want to check the presence of a tag, regardless of value, use Tag.Value.
NOTE: This is almost only ever useful for Tags of type `bool`. However, it is so useful for those that it's worth keeping, even though other Tag types can't make good use of it.
It is an alias for:
val, found := t.Value(err) return found && val == <defaultValue>
func (Tag[T]) Is ¶
Is checks to see if this Tag[T] is the same key as `other`.
This does not compare default values of `t` and `other`.
func (Tag[T]) Value ¶
Value retrieves the current value associated with this Tag in the error, also indicating if the tag was found at all.
If the tag was not found, returns (<defaultValue>, false).
func (Tag[T]) ValueOrDefault ¶
ValueOrDefault is the same as Tag.Value, but ignoring `found`.
func (Tag[T]) WithDefault ¶
WithDefault returns a variant of this Tag with a different default (for Tag.Apply, etc.).
The returned tag will have the SAME key as `t`. That is, you can probe the error with Tag.Value with either tag to see the current value. The only difference is that if the tag is entirely missing from the error, `t` will return the old default and the new tag will return the new default.
This is useful for cases where you want to have distinct Go symbols for different well know values of the tag (for example, if the tag has an enum type, you may want one tag variant per enum value so that they can be simply applied via Tag.Apply).
type TagKey ¶
type TagKey struct {
// contains filtered or unexported fields
}
TagKey is a process-unique key associated with a family of Tag[T]'s derived from the same Make/MakeWithMerge.
These are `comparable`, so e.g.
SomeTag.Key() == SomeTag.WithDefault(...).Key()
Will always compile and be `true`.