Documentation ¶
Index ¶
- func Add(ctx context.Context, kvs ...any) context.Context
- func AddAgent(ctx context.Context, name string) context.Context
- func AddComment(ctx context.Context, msg string, vs ...any) context.Context
- func AddMap[K comparable, V any](ctx context.Context, m map[K]V) context.Context
- func AddSpan(ctx context.Context, name string, kvs ...any) context.Context
- func Close(ctx context.Context) error
- func CloseSpan(ctx context.Context) context.Context
- func Comments(err error) comments
- func FromBytes(bs []byte) (*dataNode, error)
- func HasLabel(err error, label string) bool
- func In(ctx context.Context) *dataNode
- func InErr(err error) *dataNode
- func Initialize(ctx context.Context, serviceName string, config OTELConfig) (context.Context, error)
- func InjectTrace[C traceMapCarrierBase](ctx context.Context, mapCarrier C) C
- func Labels(err error) map[string]struct{}
- func NewAttribute(k string, v any) annotation
- func ReceiveTrace[C traceMapCarrierBase](ctx context.Context, mapCarrier C) context.Context
- func Relay(ctx context.Context, agent string, vs ...any)
- func Unwrap(err error) error
- type Adder
- type Annotationer
- type Err
- func Comment(err error, msg string, vs ...any) *Err
- func Label(err error, label string) *Err
- func New(msg string) *Err
- func NewWC(ctx context.Context, msg string) *Err
- func Stack(errs ...error) *Err
- func StackWC(ctx context.Context, errs ...error) *Err
- func StackWrap(sentinel, wrapped error, msg string) *Err
- func StackWrapWC(ctx context.Context, sentinel, wrapped error, msg string) *Err
- func With(err error, kvs ...any) *Err
- func WithClues(err error, ctx context.Context) *Err
- func WithMap(err error, m map[string]any) *Err
- func WithSkipCaller(err error, depth int) *Err
- func Wrap(err error, msg string) *Err
- func WrapWC(ctx context.Context, err error, msg string) *Err
- func (err *Err) As(target any) bool
- func (err *Err) Comment(msg string, vs ...any) *Err
- func (err *Err) Comments() comments
- func (err *Err) Core() *ErrCore
- func (err *Err) Error() string
- func (err *Err) Format(s fmt.State, verb rune)
- func (err *Err) HasLabel(label string) bool
- func (err *Err) Is(target error) bool
- func (err *Err) Label(labels ...string) *Err
- func (err *Err) Labels() map[string]struct{}
- func (err *Err) NoTrace() *Err
- func (err *Err) OrNil() error
- func (err *Err) SkipCaller(depth int) *Err
- func (err *Err) Unwrap() error
- func (err *Err) Values() *dataNode
- func (err *Err) With(kvs ...any) *Err
- func (err *Err) WithClues(ctx context.Context) *Err
- func (err *Err) WithMap(m map[string]any) *Err
- type ErrCore
- type OTELConfig
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AddAgent ¶
AddAgent adds an agent with a given name to the context. What's an agent? It's a special case data adder that you can spawn to collect clues for you. Unlike standard clues additions, you have to tell the agent exactly what data you want it to Relay() for you.
Agents are recorded in the current clues node and all of its descendants. Data relayed by the agent will appear as part of the standard data map, namespaced by each agent.
Agents are specifically handy in a certain set of uncommon cases where retrieving clues is otherwise difficult to do, such as working with middleware that doesn't allow control over error creation. In these cases your only option is to relay that data back to some prior clues node.
func AddComment ¶
AddComment adds a long form comment to the clues.
Comments are special case additions to the context. They're here to, well, let you add comments! Why? Because sometimes it's not sufficient to have a log let you know that a line of code was reached. Even a bunch of clues to describe system state may not be enough. Sometimes what you need in order to debug the situation is a long-form explanation (you do already add that to your code, don't you?). Or, even better, a linear history of long-form explanations, each one building on the prior (which you can't easily do in code).
Should you transfer all your comments to clues? Absolutely not. But in cases where extra explantion is truly important to debugging production, when all you've got are some logs and (maybe if you're lucky) a span trace? Those are the ones you want.
Unlike other additions, which are added as top-level key:value pairs to the context, comments are all held as a single array of additions, persisted in order of appearance, and prefixed by the file and line in which they appeared. This means comments are always added to the context and never clobber each other, regardless of their location. IE: don't add them to a loop.
func AddSpan ¶
AddSpan stacks a clues node onto this context and uses the provided name for the trace id, instead of a randomly generated hash. AddSpan can be called without additional values if you only want to add a trace marker. The assumption is that an otel span is generated and attached to the node. Callers should always follow this addition with a closing `defer clues.CloseSpan(ctx)`.
func Close ¶
Close will flush all buffered data waiting to be read. If Initialize was not called, this call is a no-op. Should be called in a defer after initializing.
func CloseSpan ¶
CloseSpan closes the current span in the clues node. Should only be called following a `clues.AddSpan()` call.
func FromBytes ¶
FromBytes deserializes the bytes to a new dataNode. No clients, agents, or hooks are initialized in this process.
func In ¶
In returns the default dataNode from the context. TODO: turn return an interface instead of a dataNode, have dataNodes and errors both comply with that wrapper.
func InErr ¶
func InErr(err error) *dataNode
InErr returns the map of contextual values in the error. Each error in the stack is unwrapped and all maps are unioned. In case of collision, lower level error data take least priority. TODO: remove this in favor of a type-independent In() that returns an interface which both dataNodes and Err comply with.
func Initialize ¶
func Initialize( ctx context.Context, serviceName string, config OTELConfig, ) (context.Context, error)
Initialize will spin up any persistent clients that are held by clues, such as OTEL communication. Clues will use these optimistically in the background to provide additional telemetry hook-ins.
Clues will operate as expected in the event of an error, or if initialization is not called. This is a purely optional step.
func InjectTrace ¶
InjectTrace adds the current trace details to the provided headers. If otel is not initialized, no-ops.
The mapCarrier is mutated by this request. The passed reference is returned mostly as a quality-of-life step so that callers don't need to declare the map outside of this call.
func NewAttribute ¶
func ReceiveTrace ¶
ReceiveTrace extracts the current trace details from the headers and adds them to the context. If otel is not initialized, no-ops.
Types ¶
type Annotationer ¶
type Err ¶
type Err struct {
// contains filtered or unexported fields
}
Err augments an error with labels (a categorization system) and data (a map of contextual data used to record the state of the process at the time the error occurred, primarily for use in upstream logging and other telemetry),
func Comment ¶
Comments are special case additions to the error. They're here to, well, let you add comments! Why? Because sometimes it's not sufficient to have an error message describe what that error really means. Even a bunch of clues to describe system state may not be enough. Sometimes what you need in order to debug the situation is a long-form explanation (you do already add that to your code, don't you?). Or, even better, a linear history of long-form explanations, each one building on the prior (which you can't easily do in code).
Unlike other additions, which are added as top-level key:value pairs to the context, the whole history of comments gets retained, persisted in order of appearance and prefixed by the file and line in which they appeared. This means comments are always added to the error and never clobber each other, regardless of their location.
func New ¶
New creates an *Err with the provided Msg.
If you have a `ctx` containing other clues data, it is recommended that you call `NewWC(ctx, msg)` to ensure that data gets added to the error.
The returned *Err is an error-compliant builder that can aggregate additional data using funcs like With(...) or Label(...).
func NewWC ¶
NewWC creates an *Err with the provided Msg, and additionally extracts all of the clues data in the context into the error.
NewWC is equivalent to clues.New("msg").WithClues(ctx).
The returned *Err is an error-compliant builder that can aggregate additional data using funcs like With(...) or Label(...).
func Stack ¶
Stack composes a stack of one or more errors. The first message in the parameters is considered the "most recent". Ex: a construction like clues.Stack(errFoo, io.EOF, errSmarf), the resulting Error message would be "foo: end-of-file: smarf".
Unwrapping a Stack follows the same order. This allows callers to inject sentinel errors into error chains (ex: clues.Stack(io.EOF, myErr)) without losing errors.Is or errors.As checks on lower errors.
If given a single error, Stack acts as a thin wrapper around the error to provide an *Err, giving the caller access to all the builder funcs and error tracing. It is always recommended that callers `return clues.Stack(err)` instead of the plain `return err`.
The returned *Err is an error-compliant builder that can aggregate additional data using funcs like With(...) or Label(...).
Stack can be given one or more `nil` error values. Nil errors will be automatically filtered from the retained stack of errors. Ex: clues.Stack(errFoo, nil, errSmarf) == clues.Stack(errFoo, errSmarf). If all input errors are nil, stack will return nil. To avoid golang footguns when returning nil structs as interfaces (such as error), callers should always return Stack().OrNil() in cases where the input error could be nil.
func StackWC ¶
StackWC composes a stack of one or more errors. The first message in the parameters is considered the "most recent". Ex: a construction like clues.StackWC(errFoo, io.EOF, errSmarf), the resulting Error message would be "foo: end-of-file: smarf".
Unwrapping a Stack follows the same order. This allows callers to inject sentinel errors into error chains (ex: clues.StackWC(io.EOF, myErr)) without losing errors.Is or errors.As checks on lower errors.
If given a single error, Stack acts as a thin wrapper around the error to provide an *Err, giving the caller access to all the builder funcs and error tracing. It is always recommended that callers `return clues.StackWC(err)` instead of the plain `return err`.
StackWC is equivalent to clues.Stack(errs...).WithClues(ctx)
The returned *Err is an error-compliant builder that can aggregate additional data using funcs like With(...) or Label(...).
Stack can be given one or more `nil` error values. Nil errors will be automatically filtered from the retained stack of errors. Ex: clues.StackWC(ctx, errFoo, nil, errSmarf) == clues.StackWC(ctx, errFoo, errSmarf). If all input errors are nil, stack will return nil. To avoid golang footguns when returning nil structs as interfaces (such as error), callers should always return StackWC().OrNil() in cases where the input error could be nil.
func StackWrap ¶
StackWrap is a quality-of-life shorthand for a common usage of clues errors: clues.Stack(sentinel, clues.Wrap(myErr, "my message")). The result follows all standard behavior of stacked and wrapped errors.
The returned *Err is an error-compliant builder that can aggregate additional data using funcs like With(...) or Label(...).
StackWrap can be given one or more `nil` error values. Nil errors will be automatically filtered from the retained stack of errors. Ex: clues.StackWrap(errFoo, nil, "msg") == clues.Wrap(errFoo, "msg"). If both input errors are nil, StackWrap will return nil. To avoid golang footguns when returning nil structs as interfaces (such as error), callers should always return StackWrap().OrNil() in cases where the input errors could be nil.
func StackWrapWC ¶
StackWrapWC is a quality-of-life shorthand for a common usage of clues errors: clues.Stack(sentinel, clues.Wrap(myErr, "my message")).WithClues(ctx). The result follows all standard behavior of stacked and wrapped errors.
The returned *Err is an error-compliant builder that can aggregate additional data using funcs like With(...) or Label(...).
StackWrapWC can be given one or more `nil` error values. Nil errors will be automatically filtered from the retained stack of errors. Ex: clues.StackWrapWC(ctx, errFoo, nil, "msg") == clues.WrapWC(ctx, errFoo, "msg"). If both input errors are nil, StackWrap will return nil. To avoid golang footguns when returning nil structs as interfaces (such as error), callers should always return StackWrap().OrNil() in cases where the input errors could be nil.
func With ¶
With adds every two values as a key,value pair to the Err's data map. If err is not an *Err intance, a new *Err is generated containing the original err.
func WithClues ¶
WithClues is syntactical-sugar that assumes you're using the clues package to store structured data in the context. The values in the default namespace are retrieved and added to the error.
clues.WithClues(err, ctx) adds the same data as clues.WithMap(err, clues.Values(ctx)).
If the context contains a clues LabelCounter, that counter is passed to the error. WithClues must always be called first in order to count labels.
func WithMap ¶
WithMap copies the map to the Err's data map. If err is not an *Err intance, returns the error wrapped into an *Err struct.
func WithSkipCaller ¶
SkipCaller skips <depth> callers when constructing the error trace stack. The caller is the file, line, and func where the *clues.Err was generated.
A depth of 0 performs no skips, and returns the same caller info as if SkipCaller was not called. 1 skips the immediate parent, etc.
Error traces are already generated for the location where clues.Wrap or clues.Stack was called. This func is for cases where Wrap or Stack calls are handled in a helper func and are not reporting the actual error origin.
If err is not an *Err intance, returns the error wrapped into an *Err struct.
func Wrap ¶
Wrap extends an error with the provided message. It is a replacement for `errors.Wrap`, and complies with all golang unwrapping behavior.
If you have a `ctx` containing other clues data, it is recommended that you call `WrapWC(ctx, err, msg)` to ensure that data gets added to the error.
The returned *Err is an error-compliant builder that can aggregate additional data using funcs like With(...) or Label(...). There is no Wrapf func in clues; we prefer that callers use Wrap().With() instead.
Wrap can be given a `nil` error value, and will return a nil *Err. To avoid golang footguns when returning nil structs as interfaces (such as error), callers should always return Wrap().OrNil() in cases where the input error could be nil.
func WrapWC ¶
WrapWC extends an error with the provided message. It is a replacement for `errors.Wrap`, and complies with all golang unwrapping behavior.
WrapWC is equivalent to clues.Wrap(err, "msg").WithClues(ctx).
If you have a `ctx` containing other clues data, it is recommended that you call `WrapWC(ctx, err, msg)` to ensure that data gets added to the error.
The returned *Err is an error-compliant builder that can aggregate additional data using funcs like With(...) or Label(...). There is no WrapWCf func in clues; we prefer that callers use WrapWC().With() instead.
Wrap can be given a `nil` error value, and will return a nil *Err. To avoid golang footguns when returning nil structs as interfaces (such as error), callers should always return WrapWC().OrNil() in cases where the input error could be nil.
func (*Err) As ¶
As overrides the standard As check for Err.e, allowing us to check the conditional for both Err.e and Err.stack. This allows clues to Stack() multiple error pointers without failing the otherwise linear errors.As check.
func (*Err) Comment ¶
Comments are special case additions to the error. They're here to, well, let you add comments! Why? Because sometimes it's not sufficient to have an error message describe what that error really means. Even a bunch of clues to describe system state may not be enough. Sometimes what you need in order to debug the situation is a long-form explanation (you do already add that to your code, don't you?). Or, even better, a linear history of long-form explanations, each one building on the prior (which you can't easily do in code).
Unlike other additions, which are added as top-level key:value pairs to the context, the whole history of comments gets retained, persisted in order of appearance and prefixed by the file and line in which they appeared. This means comments are always added to the error and never clobber each other, regardless of their location.
func (*Err) Comments ¶
func (err *Err) Comments() comments
Comments retrieves all comments in the error.
func (*Err) Core ¶
Core transforms the error into an ErrCore. ErrCore is a minimized version of an Err{}. It produces a concrete, storable version of the clues error data. Rather than expose the underlying error structure that's used for building metadata, an error core synthesizes the hierarchical storage of errors and data nodes into a flat, easily consumed set of properties.
func (*Err) Format ¶
Format ensures stack traces are printed appropariately.
%s same as err.Error() %v equivalent to %s
Format accepts flags that alter the printing of some verbs, as follows:
%+v Prints filename, function, and line number for each error in the stack.
func (*Err) Is ¶
Is overrides the standard Is check for Err.e, allowing us to check the conditional for both Err.e and Err.stack. This allows clues to Stack() multiple error pointers without failing the otherwise linear errors.Is check.
func (*Err) NoTrace ¶
NoTrace prevents the error from appearing in the trace stack. This is particularly useful for global sentinels that get stacked or wrapped into other error cases.
func (*Err) OrNil ¶
OrNil is a workaround for golang's infamous "an interface holding a nil value is not nil" gotcha. You should use it to ensure the error value to produce is properly nil whenever your wrapped or stacked error values could also possibly be nil.
ie: ``` return clues.Stack(maybeNilErrValue).OrNil() // or return clues.Wrap(maybeNilErrValue, "msg").OrNil() ```
func (*Err) SkipCaller ¶
SkipCaller skips <depth> callers when constructing the error trace stack. The caller is the file, line, and func where the *clues.Err was generated.
A depth of 0 performs no skips, and returns the same caller info as if SkipCaller was not called. 1 skips the immediate parent, etc.
Error traces are already generated for the location where clues.Wrap or clues.Stack was called. This func is for cases where Wrap or Stack calls are handled in a helper func and are not reporting the actual error origin.
func (*Err) Unwrap ¶
Unwrap provides compatibility for Go 1.13 error chains. Unwrap returns the Unwrap()ped base error, if it implements the unwrapper interface:
type unwrapper interface { Unwrap() error }
If the error does not implement Unwrap, returns the base error.
func (*Err) Values ¶
func (err *Err) Values() *dataNode
Values returns a copy of all of the contextual data in the error. Each error in the stack is unwrapped and all maps are unioned. In case of collision, lower level error data take least priority.
func (*Err) WithClues ¶
WithClues is syntactical-sugar that assumes you're using the clues package to store structured data in the context. The values in the default namespace are retrieved and added to the error.
clues.Stack(err).WithClues(ctx) adds the same data as clues.Stack(err).WithMap(clues.Values(ctx)).
If the context contains a clues LabelCounter, that counter is passed to the error. WithClues must always be called first in order to count labels.
type ErrCore ¶
type ErrCore struct { Msg string `json:"msg"` Labels map[string]struct{} `json:"labels"` Values map[string]any `json:"values"` Comments comments `json:"comments"` }
ErrCore is a minimized version of an Err{}. It produces a concrete, storable version of the clues error data. Rather than expose the underlying error structure that's used for building metadata, an error core synthesizes the hierarchical storage of errors and data nodes into a flat, easily consumed set of properties.
func ToCore ¶
ToCore transforms the error into an ErrCore. ErrCore is a minimized version of an Err{}. It produces a concrete, storable version of the clues error data. Rather than expose the underlying error structure that's used for building metadata, an error core synthesizes the hierarchical storage of errors and data nodes into a flat, easily consumed set of properties.
func (*ErrCore) Format ¶
Format provides cleaner printing of an ErrCore struct.
%s only populated values are printed, without printing the property name. %v same as %s.
Format accepts flags that alter the printing of some verbs, as follows:
%+v prints the full struct, including empty values and property names.
type OTELConfig ¶
type OTELConfig struct { // specify the endpoint location to use for grpc communication. // If empty, no telemetry exporter will be generated. // ex: localhost:4317 // ex: 0.0.0.0:4317 GRPCEndpoint string }