diff --git a/backend/src/api/mod.rs b/backend/src/api/mod.rs index fa9b84e..9f4209e 100644 --- a/backend/src/api/mod.rs +++ b/backend/src/api/mod.rs @@ -88,8 +88,8 @@ pub enum Error { #[error("invalid webmention: {0}")] InvalidWebMention(String), - #[error("can't switch to the same fronter")] - SameFronter, + #[error("can't switch to the same fronter {0}")] + SameFronter(String), } pub type Result = std::result::Result; @@ -99,7 +99,7 @@ impl<'a> Responder<'a> for Error { error!("{}", self); match self { Error::NotFound => Err(Status::NotFound), - Error::InvalidWebMention(_) | Error::SameFronter => Err(Status::BadRequest), + Error::InvalidWebMention(_) | Error::SameFronter(_) => Err(Status::BadRequest), _ => Err(Status::InternalServerError), } } diff --git a/backend/src/api/switch.rs b/backend/src/api/switch.rs index 2702e6e..33ebf7c 100644 --- a/backend/src/api/switch.rs +++ b/backend/src/api/switch.rs @@ -132,7 +132,7 @@ pub fn switch( .ok_or_else(|| Error::NotFound)?; if member.cmene == to.cmene { - return Err(Error::SameFronter); + return Err(Error::SameFronter(member.cmene)); } let now = Utc::now().naive_utc(); diff --git a/backend/src/lib.rs b/backend/src/lib.rs index a71dedb..e11501b 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -21,6 +21,7 @@ pub const APPLICATION_NAME: &str = concat!( pub mod api; pub mod models; pub mod paseto; +pub mod rocket_trace; pub mod schema; pub mod web; diff --git a/backend/src/main.rs b/backend/src/main.rs index 04025fc..5d76b96 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -11,7 +11,7 @@ use rocket_contrib::helmet::SpaceHelmet; use rocket_cors::{AllowedHeaders, AllowedOrigins}; use rocket_prometheus::PrometheusMetrics; -use ::mi::{api, paseto, web::*, MainDatabase, APPLICATION_NAME}; +use ::mi::{api, paseto, rocket_trace::*, web::*, MainDatabase, APPLICATION_NAME}; #[get("/.within/botinfo")] fn botinfo() -> &'static str { @@ -60,6 +60,7 @@ fn main() -> Result<()> { .attach(cors) .attach(MainDatabase::fairing()) .attach(SpaceHelmet::default()) + .attach(TraceRequest {}) .attach(paseto::ed25519_keypair()) .attach(DiscordWebhook::fairing()) .attach(Mastodon::fairing()) diff --git a/backend/src/rocket_trace.rs b/backend/src/rocket_trace.rs new file mode 100644 index 0000000..4463d4d --- /dev/null +++ b/backend/src/rocket_trace.rs @@ -0,0 +1,91 @@ +use rocket::fairing::{Fairing, Info, Kind}; +use rocket::http::{ContentType, Header, Method, Status}; +use rocket::{Data, Request, Response}; +use rusty_ulid::generate_ulid_string; +use std::mem; +use tracing::{span, span::Entered, Level, Span}; + +struct SpanWrapper<'a> { + span: Box, + entered: Entered<'a>, +} + +pub struct TraceRequest; + +impl Fairing for TraceRequest { + fn info(&self) -> Info { + Info { + name: "Tracing spans per request", + kind: Kind::Request | Kind::Response, + } + } + + fn on_request(&self, request: &mut Request, _: &Data) { + let reqid: String = match request.headers().get_one("X-Request-Id") { + Some(reqid) => reqid.to_string(), + None => { + let reqid = generate_ulid_string(); + request.add_header(Header::new("X-Request-Id", reqid.clone())); + reqid + } + }; + + request.local_cache(|| { + let span = span!( + Level::INFO, + "request", + method = &request.method().to_string()[..], + uri = &request.uri().to_string()[..], + addr = &request + .client_ip() + .map(|ip| ip.to_string()) + .unwrap_or("unknown".to_string())[..], + request_id = &reqid[..], + ); + let span = Box::new(span); + let entered = span.enter(); + let entered_lt = unsafe { mem::transmute::<_, Entered<'static>>(entered) }; + + Box::new(SpanWrapper { + entered: entered_lt, + span: span, + }) + }); + } + + fn on_response(&self, request: &Request, response: &mut Response) { + let span: &Box = request.local_cache(|| { + Box::new({ + let span = error_span!( + "somehow the span wasn't put into the request, neat", + method = &request.method().to_string()[..], + uri = &request.uri().to_string()[..], + address = &request + .real_ip() + .map(|ip| ip.to_string()) + .unwrap_or("unknown".to_string())[..], + ); + + let span = Box::new(span); + let entered = span.enter(); + let entered_lt = unsafe { mem::transmute::<_, Entered<'static>>(entered) }; + + SpanWrapper { + entered: entered_lt, + span: span, + } + }) + }); + match request.headers().get_one("X-Request-Id") { + Some(reqid) => { + response.set_header(Header::new("X-Request-Id", format!("{}", reqid))); + } + None => {} + }; + info!( + status_code = &response.status().code, + status_reason = response.status().reason + ); + drop(span); + } +}