PROLOGUE
I'm using async-graphql and I have hundreds of resolvers and for each resolver I would like to trace all the possible errors.
In each method of my app I'm using anyhow::{Error}.
Right now I have code similar to this for each resolver:
#[Object]
impl MutationRoot {
async fn player_create(&self, ctx: &Context<'_>, input: PlayerInput) -> Result<Player> {
let services = ctx.data_unchecked::<Services>();
let player = services
.player_create(input)
.await?;
Ok(player)
}
}
So I thought about using the below code (note the added line with .map_err()):
#[Object]
impl MutationRoot {
async fn player_create(&self, ctx: &Context<'_>, input: PlayerInput) -> Result<Player> {
let services = ctx.data_unchecked::<Services>();
let player = services
.player_create(input)
.await
.map_err(errorify)?;
Ok(player)
}
}
fn errorify(err: anyhow::Error) -> async_graphql::Error {
tracing::error!("{:?}", err);
err.into()
}
Now the error is traced along with all the error chain:
ERROR example::main:10: I'm the error number 4
Caused by:
0: I'm the error number 3
1: I'm the error number 2
2: I'm the error number 1
in example::async_graphql
QUESTION 1
Is there a way to avoid the .map_err() on each resolver?
I would like to use the ? alone.
Should I use a custom error?
Can we have a global "hook"/callback/fn to call on each error?
QUESTION 2
As you can see above the chain of the error is the inverse.
In my graphql response I'm getting as message the "I'm the error number 4" but I need to get the "I'm the error number 2" instead.
The error chain is created using anyhow like this:
- main.rs: returns error with
.with_context(|| "I'm the error number 4")? -
- call
fn player_create()ingraphql.rs: returns with.with_context(|| "I'm the error number 3")?
- call
-
-
- call
fn new_player()indomain.rs: returns with.with_context(|| "I'm the error number 2")?
- call
-
-
-
-
- call
fn save_player()indatabase.rs: returns with.with_context(|| "I'm the error number 1")?
- call
-
-
How can I accomplish this?
I'm really new to Rust. I come from Golang where I was using a struct like:
type Error struct {
error error
public_message string
}
chaining it easily with:
return fmt.Errorf("this function is called but the error was: %w", previousError)
How to do it in Rust?
Do I necessarily have to use anyhow?
Can you point me to a good handling error tutorial/book for applications?
Thank you very much.
I would suggest you define your own error for your library and handle them properly by using
thiserrorcrate.It's like Go defining
var ErrNotFound = errors.New(...)and usefmt.Errorf(..., err)to add context.With the powerful tool
enumin Rust, so you can handle every error properly bymatcharms. It also provides really convenient derive macro like#[from]or#[error(transparent)]to make error conversion/forwarding easy.Edit 1: If you want to separate public message and tracing log, you may consider defining you custom error struct like this:
and implement
Displaytrait for formatting its innermsgfield.Finally, you could use macro in
tracing-attributescrate: