This repository contains all common code for handling errors across our various services. It is part of StreamingFast.
- Use
derr.Wrap(err, "some message")
to wrap any calls to a sub-system that could have yielded somederr.ErrorResponse
(so it is passed to the user untouched). - Use
derr.SomeError
(seeerrors.go
) wherever possible to have consistent errors throughout our system. If you think the error you are creating can be shared, put it inerrors.go
and use that. - Craft your own custom Error builder (in your project's
errors.go
, see eosws) and use that in your code. Craft a meaningfulderr.C
(orderr.ErrorCode
) with a good name (see below), reuse where possible.
We create derr.Status
errors when we want to set a specific error
code. We call derr.Wrap
to prefix the error message going out
(without altering the outgoing Status Code).
Returning plain fmt.Errorf()
upstream implies returning a
codes.Internal
gRPC status code.
Missing a derr.Wrap()
kills the links and removes the StatusCode, so
use Wrap
(or Wrapf
) whenever you want to add a prefix.
All error should be created by defining a properly named function that receives a context.Context
object as well as specialized parameters to craft a proper error message and details. This specific
error function should be re-using under the cover one the underlying pre-defined HTTP error creator,
see list.
The most important thing is that all error should have a proper unique code defined using
derr.C("value")
type alias which is a sugar syntax for derr.ErrorCode("string_code_error")
.
This is of the uttermost importance so that we can easily grep all our source code files to extract the used specific error codes across all our services for documentation purposes.
The string code defined must be unique among all our services, human readable,
should clearly represent the error in few words, should be in snake_case
format and
should end with _error
.
For example, let's say that in your micro service, you would like to define a specialized bad request error when a particular request is invalid due to block number being too low.
Here the piece of code that your should do:
func BlockNumTooLowError(ctx context.Context, blockNum uint32, thresholdBlockNum uint32) *derr.ErrorResponse {
return HTTPBadRequestError(ctx, err, derr.C("block_num_too_low_error"), "The requested block num is too low",
"actual_block_num", blockNum,
"threshold_block_num", thresholdBlockNum,
)
}
Each of generic HTTP error creator receives the context.Context
object. This context is required to
extract the traceID
from the context so that the trace ID is returned back to the user for future
analysis of the problem.
Moreover, the idiomatic way to group errors is to put them all in a file errors.go
in the root package
of the service so they are all easily discoverable in a single location.
Here the explained JSON format:
{
"code": "specifc_error_code",
"trace_id": "%s",
"message": "Sepcific error code message, audience is the end user."
"details": {
"key": <value>,
...
},
}
Parameter | Explanation |
---|---|
code |
The unique error representing this error, should be a human readable summary of the error, in snake case. |
trace_id |
The unique trace id to further debug that error, the trace id can also be correlated in the logs. |
message |
A message describing the error. The audience of the message is the end user. Should be a full sentence ending with a dot. |
details |
A key-value map of extra details specific to the error. Usually contains faulty parameters and extra details about the error. |
This package is aware of wrapped errors through the github.com/pkg/errors package.
As a convenience, the package provides shortcuts to the Wrap
and Wrapf of the pkgErrors
package so you don't need to import it directly using a custom name like pkgErrors
(to avoid
conflict with the standard errors
package).
You can simply use derr.Wrap
and derr.Wrapf
to wrap your errors with newer contextual
errors.
The package provides a facility to write any error
object back to the user. The
derr.WriteError(ctx context.Context, w http.ResponseWriter, message string, err error)
will correctly
find the derr.ErrorResponse
out of the err
parameter received and will output to the user.
If no derr.ErrorResponse
can be found, automatically, the error is wrapped in an derr.UnexpectedError
and returned like that to the user. This behavior will result in a generic error message appearing
for the user.
Note that WriteError
is also logging the error at the same occasion, removing the burden to
log the error yourself. If the error written back to the user generates a >= 500
error code,
the Error
level is used. Otherwise, a Debug
level is used to log the error.
This error logging will ultimately trickle down to our monitoring infrastructure, so if you use
WriteError
, be sure to not log it again!
Issues and PR in this repo related strictly to the derr library.
Report any protocol-specific issues in their respective repositories
Please first refer to the general StreamingFast contribution guide, if you wish to contribute to this code base.