macro_rules! reqwest_otel_span {
    (name=$name:expr, $request:ident) => { ... };
    (level=$level:expr, name=$name:expr, $request:ident) => { ... };
    (name=$name:expr, $request:ident, $($field:tt)*) => { ... };
    (level=$level:expr, name=$name:expr, $request:ident, $($field:tt)*) => { ... };
}
Expand description

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 is a span name. The second argument passed to reqwest_otel_span! is a reference to an reqwest::Request.

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!(name = "reqwest-http-request", 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. Note that to avoid leaking sensitive information, the macro doesn’t include http.url, even though it’s required by opentelemetry. You can add the URL attribute explicitly by using SpanBackendWithUrl instead of DefaultSpanBackend or adding the field on your own implementation.

You can define new fields following the same syntax of tracing::info_span! for fields:

use reqwest_tracing::reqwest_otel_span;

// Define a `time_elapsed` field as empty. It might be populated later.
// (This example is just to show how to inject data - otel already tracks durations)
reqwest_otel_span!(name = "reqwest-http-request", request, time_elapsed = tracing::field::Empty);

// Define a `name` field with a known value, `AppName`.
reqwest_otel_span!(name = "reqwest-http-request", request, name = "AppName");

// Define an `app_id` field using the variable with the same name as value.
let app_id = "XYZ";
reqwest_otel_span!(name = "reqwest-http-request", request, app_id);

// All together
reqwest_otel_span!(name = "reqwest-http-request", request, time_elapsed = tracing::field::Empty, name = "AppName", app_id);

You can also choose to customise the level of the generated span:

use reqwest_tracing::reqwest_otel_span;
use tracing::Level;

// Reduce the log level for service endpoints/probes
let level = if request.method().as_str() == "POST" {
    Level::DEBUG
} else {
    Level::INFO
};

// `level =` and name MUST come before the request, in this order
reqwest_otel_span!(level = level, name = "reqwest-http-request", request);