Skip to content

HTTPX

See every HTTP request your app makes with HTTPX: the URL, the response status, how long it took, and any errors, as a span (one unit of work with a name, a start, and a duration) in Logfire. Related spans link together into a trace (the full journey of one request), so a slow outgoing call shows up right next to the code that triggered it.

This works with both the synchronous httpx.Client and the asynchronous httpx.AsyncClient.

What you’ll capture

  • Each request as a span, with its URL, method, response status, and duration
  • Any errors that occurred during the request
  • Optionally, request and response headers and bodies (off by default: see below)

Before you start

You’ll need a Logfire project. Open Add data in your project (top navigation) and follow the setup for your language: it signs your machine in with logfire auth (a browser sign-in, no token to copy) and, for production or other languages, creates a write token (the credential your app uses to send data). New to Logfire? Start with Getting Started.

Installation

Install logfire with the httpx extra:

Terminal
pip install 'logfire[httpx]'

Usage

Add two lines to your app: logfire.configure() to connect to your project, and logfire.instrument_httpx() to record every request.

main.py
import asyncio

import httpx

import logfire

logfire.configure()
logfire.instrument_httpx()

url = 'https://httpbin.org/get'

with httpx.Client() as client:
    client.get(url)


async def main():
    async with httpx.AsyncClient() as client:
        await client.get(url)


asyncio.run(main())

Run it with python main.py.

Verify it worked

Run your program, then open your project in the Logfire web app and go to the Live view. Within a few seconds you should see a span for the GET request. Click it to see the URL, response status, and how long it took.

Troubleshooting

Not seeing your requests in Logfire? Check these first:

  • logfire.configure() runs before logfire.instrument_httpx(). Configure the connection first, then instrument.
  • You instrument the client you actually call. instrument_httpx() with no argument covers all clients; if you pass a specific client, make sure it’s the one making the request.
  • Your write token is set. In local development, run logfire projects use <your-project>; in production, set the LOGFIRE_TOKEN environment variable. See Getting Started.
  • You actually made a request. Spans appear only after a request completes.

Advanced

The logfire.instrument_httpx() method accepts several parameters to control what’s captured.

Capture everything

Capture all request and response headers and bodies by setting capture_all=True. This sends that data to Logfire, so avoid it if your requests carry secrets or personally identifiable information (PII).

import httpx

import logfire

logfire.configure()
logfire.instrument_httpx(capture_all=True)

client = httpx.Client()
client.post('https://httpbin.org/post', json={'key': 'value'})

Capture HTTP headers

By default, Logfire doesn’t record HTTP headers. Turn them on with capture_headers=True:

import httpx

import logfire

logfire.configure()
logfire.instrument_httpx(capture_headers=True)

client = httpx.Client()
client.get('https://httpbin.org/get')

Capture only request headers

Instead of capturing both request and response headers, you can use a request hook to capture only the request headers:

import httpx
from opentelemetry.trace import Span

import logfire
from logfire.integrations.httpx import RequestInfo


def capture_request_headers(span: Span, request: RequestInfo):
    headers = request.headers
    span.set_attributes(
        {f'http.request.header.{header_name}': headers.get_list(header_name) for header_name in headers.keys()}
    )


logfire.configure()
logfire.instrument_httpx(request_hook=capture_request_headers)

client = httpx.Client()
client.get('https://httpbin.org/get')

Capture only response headers

Similarly, use a response hook to capture only the response headers:

import httpx
from opentelemetry.trace import Span

import logfire
from logfire.integrations.httpx import RequestInfo, ResponseInfo


def capture_response_headers(span: Span, request: RequestInfo, response: ResponseInfo):
    headers = response.headers
    span.set_attributes(
        {f'http.response.header.{header_name}': headers.get_list(header_name) for header_name in headers.keys()}
    )


logfire.configure()
logfire.instrument_httpx(response_hook=capture_response_headers)

client = httpx.Client()
client.get('https://httpbin.org/get')

Inside a hook you choose which headers to record on the span. If you also set capture_headers=True, though, Logfire records the headers before your hook runs, so a hook can’t redact those after the fact; use scrubbing for that.

Capture HTTP bodies

By default, Logfire doesn’t record HTTP bodies. Turn them on with capture_request_body and capture_response_body. As with headers, this sends the body data to Logfire, so avoid it for requests that carry sensitive data.

import httpx

import logfire

logfire.configure()
logfire.instrument_httpx(
    capture_request_body=True,
    capture_response_body=True,
)

client = httpx.Client()
client.post('https://httpbin.org/post', data='Hello, World!')

Reference

Excluding URLs from instrumentation