diff --git a/src/lib.rs b/src/lib.rs index 4cb07bdd4..62d071666 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![recursion_limit = "512"] pub mod api_routes; pub mod code_migrations; +pub mod root_span_builder; pub mod scheduled_tasks; use lemmy_utils::LemmyError; @@ -13,7 +14,7 @@ pub fn init_tracing() -> Result<(), LemmyError> { LogTracer::init()?; let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); - let format_layer = tracing_subscriber::fmt::layer().pretty(); + let format_layer = tracing_subscriber::fmt::layer(); let subscriber = Registry::default() .with(env_filter) diff --git a/src/main.rs b/src/main.rs index 88eaba83d..8f130fde2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ use lemmy_server::{ api_routes, code_migrations::run_advanced_migrations, init_tracing, + root_span_builder::QuieterRootSpanBuilder, scheduled_tasks, }; use lemmy_utils::{ @@ -124,7 +125,7 @@ async fn main() -> Result<(), LemmyError> { let rate_limiter = rate_limiter.clone(); App::new() .wrap(actix_web::middleware::Logger::default()) - .wrap(TracingLogger::default()) + .wrap(TracingLogger::::new()) .app_data(Data::new(context)) // The routes .configure(|cfg| api_routes::config(cfg, &rate_limiter)) diff --git a/src/root_span_builder.rs b/src/root_span_builder.rs new file mode 100644 index 000000000..b2db016c2 --- /dev/null +++ b/src/root_span_builder.rs @@ -0,0 +1,71 @@ +use actix_web::{http::StatusCode, ResponseError}; +use tracing::Span; +use tracing_actix_web::RootSpanBuilder; + +// Code in this module adapted from DefaultRootSpanBuilder +// https://github.com/LukeMathWalker/tracing-actix-web/blob/main/src/root_span_builder.rs +// and root_span! +// https://github.com/LukeMathWalker/tracing-actix-web/blob/main/src/root_span_macro.rs + +pub struct QuieterRootSpanBuilder; + +impl RootSpanBuilder for QuieterRootSpanBuilder { + fn on_request_start(request: &actix_web::dev::ServiceRequest) -> Span { + let request_id = tracing_actix_web::root_span_macro::private::get_request_id(request); + + tracing::info_span!( + "HTTP request", + http.method = %request.method(), + http.scheme = request.connection_info().scheme(), + http.host = %request.connection_info().host(), + http.target = %request.uri().path(), + http.status_code = tracing::field::Empty, + otel.kind = "server", + otel.status_code = tracing::field::Empty, + trace_id = tracing::field::Empty, + request_id = %request_id, + exception.message = tracing::field::Empty, + // Not proper OpenTelemetry, but their terminology is fairly exception-centric + exception.details = tracing::field::Empty, + ) + } + + fn on_request_end( + span: tracing::Span, + outcome: &Result, actix_web::Error>, + ) { + match &outcome { + Ok(response) => { + if let Some(error) = response.response().error() { + // use the status code already constructed for the outgoing HTTP response + handle_error(span, response.status(), error.as_response_error()); + } else { + let code: i32 = response.response().status().as_u16().into(); + span.record("http.status_code", &code); + span.record("otel.status_code", &"OK"); + } + } + Err(error) => { + let response_error = error.as_response_error(); + handle_error(span, response_error.status_code(), response_error); + } + }; + } +} + +fn handle_error(span: Span, status_code: StatusCode, response_error: &dyn ResponseError) { + // pre-formatting errors is a workaround for https://github.com/tokio-rs/tracing/issues/1565 + let display = format!("{}", response_error); + let debug = format!("{:?}", response_error); + span.record("exception.message", &tracing::field::display(display)); + span.record("exception.details", &tracing::field::display(debug)); + let code: i32 = status_code.as_u16().into(); + + span.record("http.status_code", &code); + + if status_code.is_client_error() { + span.record("otel.status_code", &"OK"); + } else { + span.record("otel.status_code", &"ERROR"); + } +}