#![cfg_attr(not(feature = "sentry"), allow(unused_variables))]
#[cfg(feature = "sentry")]
pub mod sentry;
use std::fmt::Debug;
use error_stack::{Context, Report};
use serde::Serialize;
use crate::errors::HttpError;
#[derive(Default)]
pub enum ErrorReporter {
#[cfg(feature = "sentry")]
Sentry,
#[default]
Noop,
}
impl ErrorReporter {
pub async fn send_error<E: std::error::Error + Send>(&self, err: &E) {
match self {
#[cfg(feature = "sentry")]
ErrorReporter::Sentry => sentry::Sentry::send_error(err),
ErrorReporter::Noop => {}
}
}
pub async fn send_report<C: Context>(&self, err: &Report<C>) {
match self {
#[cfg(feature = "sentry")]
ErrorReporter::Sentry => sentry::Sentry::send_report(err),
ErrorReporter::Noop => {}
}
}
pub async fn send_error_with_metadata<E: std::error::Error + Send, T: Serialize + Send>(
&self,
err: &E,
metadata: &T,
) {
match self {
#[cfg(feature = "sentry")]
ErrorReporter::Sentry => sentry::Sentry::send_error_with_metadata(err, metadata),
ErrorReporter::Noop => {}
}
}
pub async fn send_report_with_metadata<C: Context, T: Serialize + Send>(
&self,
err: &error_stack::Report<C>,
metadata: &T,
) {
match self {
#[cfg(feature = "sentry")]
ErrorReporter::Sentry => sentry::Sentry::send_report_with_metadata(err, metadata),
ErrorReporter::Noop => {}
}
}
pub async fn send_message(&self, level: tracing::Level, message: &str) {
match self {
#[cfg(feature = "sentry")]
ErrorReporter::Sentry => sentry::Sentry::send_message(level, message),
ErrorReporter::Noop => {}
}
}
}
pub trait HandleError {
fn report_error(self) -> Self;
fn report_error_with_info<META: Debug + Serialize + Send + Sync>(self, meta: &META) -> Self;
fn status_code(&self) -> hyper::StatusCode;
}
impl<T, E> HandleError for Result<T, E>
where
E: HttpError + std::error::Error + Sync + Send,
{
fn report_error(self) -> Self {
self.report_error_with_info(&())
}
fn report_error_with_info<META: Debug + Serialize + Send + Sync>(self, meta: &META) -> Self {
if let Err(error) = &self {
tracing::error!(?error, ?meta);
let code = error.status_code();
if code.is_server_error() {
#[cfg(feature = "sentry")]
sentry::Sentry::send_error_with_metadata(&error, meta);
}
}
self
}
fn status_code(&self) -> hyper::StatusCode {
match self {
Ok(_) => hyper::StatusCode::OK,
Err(error) => error.status_code(),
}
}
}
pub trait HandleErrorReport {
fn report_error(self) -> Self;
fn report_error_with_info<META: Debug + Serialize + Send + Sync>(self, meta: &META) -> Self;
fn status_code(&self) -> hyper::StatusCode;
}
impl<T, E> HandleErrorReport for Result<T, error_stack::Report<E>>
where
E: HttpError + std::error::Error + Sync + Send + 'static,
{
fn report_error(self) -> Self {
self.report_error_with_info(&())
}
fn report_error_with_info<META: Debug + Serialize + Send + Sync>(self, meta: &META) -> Self {
if let Err(error) = &self {
tracing::error!(error = ?error, ?meta);
let code = error.current_context().status_code();
if code.is_server_error() {
#[cfg(feature = "sentry")]
sentry::Sentry::send_report_with_metadata(error, meta);
}
}
self
}
fn status_code(&self) -> hyper::StatusCode {
match self {
Ok(_) => hyper::StatusCode::OK,
Err(error) => error.current_context().status_code(),
}
}
}