How do I enable logging for the Reqwest client?

Overview

Enabling logging for the Reqwest client helps debug HTTP requests, monitor network activity, and troubleshoot connection issues. Rust provides several logging options through the log facade and modern tracing ecosystem.

Method 1: Using env_logger (Simple Setup)

Dependencies

Add these dependencies to your Cargo.toml:

[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1.0", features = ["full"] }
log = "0.4"
env_logger = "0.10"

Basic Implementation

use log::{info, debug};
use reqwest::Client;
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // Initialize logger with default settings
    env_logger::init();

    let client = Client::new();

    info!("Making request to httpbin.org");
    let response = client
        .get("https://httpbin.org/get")
        .header("User-Agent", "my-rust-app/1.0")
        .send()
        .await?;

    info!("Response status: {}", response.status());
    debug!("Response headers: {:?}", response.headers());

    Ok(())
}

Environment Variables

Control logging levels with RUST_LOG:

# Enable all debug logs
RUST_LOG=debug cargo run

# Only Reqwest debug logs
RUST_LOG=reqwest=debug cargo run

# Multiple targets with different levels
RUST_LOG=reqwest=debug,hyper=info cargo run

# Enable trace-level logging for detailed HTTP info
RUST_LOG=reqwest=trace cargo run

Method 2: Using tracing (Recommended)

The tracing crate provides more advanced logging capabilities:

Dependencies

[dependencies]
reqwest = { version = "0.11", features = ["json", "tracing"] }
tokio = { version = "1.0", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

Implementation

use reqwest::Client;
use tracing::{info, debug, instrument};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize tracing
    tracing_subscriber::registry()
        .with(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "reqwest=debug,hyper=info".into()),
        )
        .with(tracing_subscriber::fmt::layer())
        .init();

    make_request().await?;
    Ok(())
}

#[instrument]
async fn make_request() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new();

    info!("Starting HTTP request");

    let response = client
        .post("https://httpbin.org/post")
        .json(&serde_json::json!({
            "name": "John Doe",
            "email": "john@example.com"
        }))
        .send()
        .await?;

    info!(status = %response.status(), "Request completed");
    debug!(headers = ?response.headers(), "Response headers");

    let body: serde_json::Value = response.json().await?;
    debug!(response_body = %body, "Response body");

    Ok(())
}

Method 3: Custom Logger Configuration

For more control over logging format and output:

use env_logger::{Builder, Target};
use log::LevelFilter;
use std::io::Write;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Custom logger configuration
    Builder::from_default_env()
        .target(Target::Stdout)
        .filter_level(LevelFilter::Debug)
        .format(|buf, record| {
            writeln!(buf,
                "{} [{}] - {} - {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.level(),
                record.target(),
                record.args()
            )
        })
        .init();

    let client = reqwest::Client::builder()
        .user_agent("my-app/1.0")
        .timeout(std::time::Duration::from_secs(10))
        .build()?;

    // The client will now log detailed request/response info
    let response = client
        .get("https://api.github.com/users/octocat")
        .send()
        .await?;

    println!("User data: {}", response.text().await?);
    Ok(())
}

Logging Output Examples

With RUST_LOG=reqwest=debug, you'll see output like:

DEBUG reqwest::connect: starting new connection: https://httpbin.org/
DEBUG reqwest::async_impl::client: response '200 OK' for https://httpbin.org/get
DEBUG reqwest::async_impl::decoder: decoding response body

With RUST_LOG=hyper=debug, you'll get lower-level HTTP details:

DEBUG hyper::client::connect::http: connecting to 54.175.219.8:443
DEBUG hyper::client::connect::http: connected to 54.175.219.8:443
DEBUG hyper::proto::h1::io: flushed 78 bytes

Production Configuration

For production environments, consider structured logging:

use tracing_subscriber::fmt::format::FmtSpan;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    tracing_subscriber::fmt()
        .with_env_filter("reqwest=info,my_app=debug")
        .with_span_events(FmtSpan::CLOSE)
        .json()  // JSON format for log aggregation
        .init();

    // Your application code here
    Ok(())
}

Common Log Levels

  • TRACE: Very detailed, includes request/response bodies
  • DEBUG: Request URLs, headers, timing information
  • INFO: High-level request/response status
  • WARN: Retry attempts, timeouts
  • ERROR: Failed requests, connection errors

Enable the appropriate level based on your debugging needs while being mindful of performance and log volume in production.

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon