noiseless_tracing_actix_web/
lib.rs1use std::borrow::Cow;
2use actix_web::body::MessageBody;
3use actix_web::dev::{ServiceRequest, ServiceResponse};
4use actix_web::{Error, ResponseError};
5use actix_web::http::{Method, StatusCode, Version};
6use tracing::Span;
7use tracing_actix_web::RootSpanBuilder;
8
9#[derive(Clone, Copy)]
11pub struct NoiselessRootSpanBuilder;
12
13impl RootSpanBuilder for NoiselessRootSpanBuilder {
14 #[allow(unused)] fn on_request_start(request: &ServiceRequest) -> Span {
16 let http_route: Cow<'static, str> = request
17 .match_pattern()
18 .map(Into::into)
19 .unwrap_or_else(|| "default".into());
20 let http_method = http_method_str(&request.method());
21 let http_flavor = http_flavor(request.version());
22
23 let span = ::tracing::span!(
24 ::tracing::Level::INFO,
25 "HTTP request",
26 http.method = %http_method,
27 http.route = %http_route,
28 http.flavor = %http_flavor,
29 http.status_code = ::tracing::field::Empty,
30 exception.message = ::tracing::field::Empty,
31 exception.details = ::tracing::field::Empty,
32 );
33 span
34 }
35
36 fn on_request_end<B: MessageBody>(span: Span, outcome: &Result<ServiceResponse<B>, Error>) {
37 match &outcome {
38 Ok(response) => {
39 if let Some(error) = response.response().error() {
40 handle_error(span, response.status(), error.as_response_error());
42 } else {
43 let code: i32 = response.response().status().as_u16().into();
44 span.record("http.status_code", code);
45 span.record("otel.status_code", "OK");
46 }
47 }
48 Err(error) => {
49 let response_error = error.as_response_error();
50 handle_error(span, response_error.status_code(), response_error);
51 }
52 };
53 }
54}
55
56fn handle_error(span: Span, status_code: StatusCode, response_error: &dyn ResponseError) {
57 let display = format!("{response_error}");
59 let debug = format!("{response_error:?}");
60 span.record("exception.message", &tracing::field::display(display));
61 span.record("exception.details", &tracing::field::display(debug));
62 let code: i32 = status_code.as_u16().into();
63
64 span.record("http.status_code", code);
65}
66
67fn http_flavor(version: Version) -> Cow<'static, str> {
68 match version {
69 Version::HTTP_09 => "0.9".into(),
70 Version::HTTP_10 => "1.0".into(),
71 Version::HTTP_11 => "1.1".into(),
72 Version::HTTP_2 => "2.0".into(),
73 Version::HTTP_3 => "3.0".into(),
74 other => format!("{other:?}").into(),
75 }
76}
77
78fn http_method_str(method: &Method) -> Cow<'static, str> {
79 match method {
80 &Method::OPTIONS => "OPTIONS".into(),
81 &Method::GET => "GET".into(),
82 &Method::POST => "POST".into(),
83 &Method::PUT => "PUT".into(),
84 &Method::DELETE => "DELETE".into(),
85 &Method::HEAD => "HEAD".into(),
86 &Method::TRACE => "TRACE".into(),
87 &Method::CONNECT => "CONNECT".into(),
88 &Method::PATCH => "PATCH".into(),
89 other => other.to_string().into(),
90 }
91}