#[macro_export] /// [`reqwest_otel_span!`] creates a new [`tracing::Span`]. /// It empowers you to add custom properties to the span on top of the default properties provided by the macro /// /// Default Fields: /// - http.method /// - http.scheme /// - http.host /// - net.host /// - otel.kind /// - otel.name /// - otel.status_code /// - http.user_agent /// - http.status_code /// - error.message /// - error.cause_chain /// /// Here are some convenient functions to checkout [`default_on_request_success`], [`default_on_request_failure`], /// and [`default_on_request_end`]. /// /// # Why a macro? /// /// [`tracing`] requires all the properties attached to a span to be declared upfront, when the span is created. /// You cannot add new ones afterwards. /// This makes it extremely fast, but it pushes us to reach for macros when we need some level of composition. /// /// # Macro syntax /// /// The first argument passed to [`reqwest_otel_span!`] is a reference to an [`reqwest::Request`]. /// /// ```rust /// use reqwest_middleware::Result; /// use task_local_extensions::Extensions; /// use reqwest::{Request, Response}; /// use reqwest_tracing::{ /// default_on_request_end, reqwest_otel_span, ReqwestOtelSpanBackend /// }; /// use tracing::Span; /// /// pub struct CustomReqwestOtelSpanBackend; /// /// impl ReqwestOtelSpanBackend for CustomReqwestOtelSpanBackend { /// fn on_request_start(req: &Request, _extension: &mut Extensions) -> Span { /// reqwest_otel_span!(req) /// } /// /// fn on_request_end(span: &Span, outcome: &Result, _extension: &mut Extensions) { /// default_on_request_end(span, outcome) /// } /// } /// ``` /// /// If nothing else is specified, the span generated by `reqwest_otel_span!` is identical to the one you'd /// get by using [`DefaultSpanBackend`]. /// /// You can define new fields following the same syntax of [`tracing::info_span!`] for fields: /// /// ```rust,should_panic /// use reqwest_tracing::reqwest_otel_span; /// # let request: &reqwest::Request = todo!(); /// /// // Define a `time_elapsed` field as empty. It might be populated later. /// reqwest_otel_span!(request, time_elapsed = tracing::field::Empty); /// /// // Define a `name` field with a known value, `AppName`. /// reqwest_otel_span!(request, name = "AppName"); /// /// // Define an `app_id` field using the variable with the same name as value. /// let app_id = "XYZ"; /// reqwest_otel_span!(request, app_id); /// /// // All together /// reqwest_otel_span!(request, time_elapsed = tracing::field::Empty, name = "AppName", app_id); /// ``` /// /// You can also choose to customise the level of the generated span: /// /// ```rust,should_panic /// use reqwest_tracing::reqwest_otel_span; /// use tracing::Level; /// # let request: &reqwest::Request = todo!(); /// /// // Reduce the log level for service endpoints/probes /// let level = if request.method().as_str() == "POST" { /// Level::DEBUG /// } else { /// Level::INFO /// }; /// /// // `level =` MUST be the first argument. /// reqwest_otel_span!(level = level, request); /// ``` /// /// /// [`DefaultSpanBackend`]: crate::reqwest_otel_span_builder::DefaultSpanBackend /// [`default_on_request_success`]: crate::reqwest_otel_span_builder::default_on_request_success /// [`default_on_request_failure`]: crate::reqwest_otel_span_builder::default_on_request_failure /// [`default_on_request_end`]: crate::reqwest_otel_span_builder::default_on_request_end macro_rules! reqwest_otel_span { // Vanilla root span at default INFO level, with no additional fields ($request:ident) => { reqwest_otel_span!($request,) }; // Vanilla root span, with no additional fields but custom level (level=$level:expr, $request:ident) => { reqwest_otel_span!(level=$level, $request,) }; // Root span with additional fields, default INFO level ($request:ident, $($field:tt)*) => { reqwest_otel_span!(level=$crate::reqwest_otel_span_macro::private::Level::INFO, $request, $($field)*) }; // Root span with additional fields and custom level (level=$level:expr, $request:ident, $($field:tt)*) => { { let method = $request.method(); let scheme = $request.url().scheme(); let host = $request.url().host_str().unwrap_or(""); let host_port = $request.url().port().unwrap_or(0) as i64; let path = $request.url().path(); let otel_name = format!("{} {}", method, path); macro_rules! request_span { ($lvl:expr) => { $crate::reqwest_otel_span_macro::private::span!( $lvl, "HTTP request", http.method = %method, http.scheme = %scheme, http.host = %host, net.host.port = %host_port, otel.kind = "client", otel.name = %otel_name, otel.status_code = tracing::field::Empty, http.user_agent = tracing::field::Empty, http.status_code = tracing::field::Empty, error.message = tracing::field::Empty, error.cause_chain = tracing::field::Empty, $($field)* ) } } let span = match $level { $crate::reqwest_otel_span_macro::private::Level::TRACE => { request_span!($crate::reqwest_otel_span_macro::private::Level::TRACE) }, $crate::reqwest_otel_span_macro::private::Level::DEBUG => { request_span!($crate::reqwest_otel_span_macro::private::Level::DEBUG) }, $crate::reqwest_otel_span_macro::private::Level::INFO => { request_span!($crate::reqwest_otel_span_macro::private::Level::INFO) }, $crate::reqwest_otel_span_macro::private::Level::WARN => { request_span!($crate::reqwest_otel_span_macro::private::Level::WARN) }, $crate::reqwest_otel_span_macro::private::Level::ERROR => { request_span!($crate::reqwest_otel_span_macro::private::Level::ERROR) }, }; span } } } #[doc(hidden)] pub mod private { #[doc(hidden)] pub use tracing::{span, Level}; }