forked from mirror/reqwest-middleware
13ca870d20
Provides reqwest-tracing with a trait ReqwestOtelSpanBackend, allowing custom fields/behaviour to be defined. This is a breaking change as the TracingMiddleware signature now requires you to define a ReqwestOtelSpanBackend to provide it with the desired span behaviour. Co-authored-by: Alessandro Zanin <ale.zanin90@gmail.com> Co-authored-by: tl-flavio-barinas <flavio.barinas@truelayer.com> Co-authored-by: Marco Tormento <tl-marco-tormento@users.noreply.github.com>
169 lines
6.4 KiB
Rust
169 lines
6.4 KiB
Rust
#[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<Response>, _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};
|
|
}
|