# What gets recorded

> Everything through the Http facade, anything on a Guzzle stack you instrument, and nothing on a host you ignore.

Source: https://requizon.boring-observability.dev/docs/what-gets-recorded
Section: Configuration — Requizon documentation (version 0.2)
Updated: 2026-09-16

---

Requizon records at the transport. It adds one middleware to Laravel's HTTP client, and the same middleware can be pushed onto any Guzzle handler stack. Whatever passes through it is recorded unless its host is on the ignore list. Nothing at the call site decides.

## Laravel's HTTP client

With `instrument.laravel_client` on (the default), Requizon registers its middleware as global middleware on `Illuminate\Http\Client\Factory`. Every request the factory builds carries it, which covers:

- `Http::get()`, `Http::post()` and every other call through the facade
- `Http::pool()`, where each request in the pool is recorded on its own
- A `Factory` injected through the container rather than the facade
- Packages and SDKs that make their calls through Laravel's HTTP client

The factory is only hooked when something resolves it, so a request that makes no outbound calls never constructs the HTTP client. Under Octane the middleware is installed once per factory, not once per request.

### Transfers, not calls

The middleware sits inside Guzzle's redirect handling, and Laravel's `retry()` sends the whole request again. A call that follows two redirects is recorded as three rows, and a call retried twice is recorded as three rows. Each hop had its own latency and could fail in its own way, so each gets a row. Expect the dashboard's totals to be higher than the number of calls your code thinks it made.

## Guzzle clients and SDKs

An SDK that builds its own Guzzle client never touches Laravel's HTTP client, so it is not recorded until you push the middleware onto its handler stack:

```php
use BoringO11y\Requizon\Guzzle\RecordHttpRequests;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;

$stack = HandlerStack::create();
$stack->push(app(RecordHttpRequests::class));

$client = new Client(['handler' => $stack]);
```

Most SDKs that accept a Guzzle client or handler take it as a constructor option. Resolve the middleware from the container with `app()`, as above, so it uses the same recorder and configuration as everything else.

Pushing it onto a stack whose requests also pass through the Laravel client's copy is safe. The first copy marks the request's options, the second sees the mark and steps aside, and the transfer is recorded once.

To record *only* the stacks you instrument by hand, turn the global hook off:

```bash
REQUIZON_INSTRUMENT_LARAVEL=false
```

## Ignoring hosts

`ignore_hosts` is a list of `Str::is()` patterns matched against the request's hostname, without the scheme or port. A matching call is not recorded at all: no row, no failure detection, no body read.

```php
// config/requizon.php
'ignore_hosts' => [
    '*.datadoghq.com',
    '*.ingest.sentry.io',
    'logs.example.internal',
    'localhost',
],
```

> **Put your telemetry backends here first**
>
> An exporter that ships logs, spans or metrics over HTTP through Laravel's client is outbound traffic like any other. Left in, it records a row for every batch it ships, and on a busy application the telemetry becomes the largest API on the dashboard.

Hosts are the only thing the ignore list matches. To drop some paths of an API while keeping others, collapse them with [`paths.patterns`](https://requizon.boring-observability.dev/docs/paths#patterns) instead.

## How a call is recorded

Recording hooks Guzzle's `on_stats` option, which fires once per transfer after the response has arrived and before your code sees it. It is also the only hook that fires when there was no response at all, which is how a DNS failure or a timeout gets a row. The duration is cURL's own transfer time, so a request that waited in a `pool()` behind the concurrency limit is not charged for the wait.

If your code already passed an `on_stats` callback, it still runs, and it runs first.

### Recording never breaks the call

Guzzle turns an exception thrown inside `on_stats` into a failure of the request itself. So every step of recording is wrapped: an exception is passed to your application's exception handler and then swallowed, and if the exception handler itself throws (a logging channel that cannot be built during a database outage, say), that is swallowed too. The price is that a problem inside Requizon shows up in your error tracker, not as a failed call.

### The promise fallback

A few handlers never invoke `on_stats`: some SDK shims, and some of the stub shapes `Http::fake()` accepts. With `instrument.promise_fallback` on, Requizon notices when a transfer finished without stats and records it anyway, timed by the wall clock. That time includes any wait in a request pool, so treat it as a guarantee the call is not lost rather than as a measurement.

## In your test suite

`Http::fake()` responses go through the same middleware, so a test suite with Requizon enabled writes rows to `http_requests`, with a duration of 0 ms. Turn it off for tests:

```xml
<!-- phpunit.xml -->
<env name="REQUIZON_ENABLED" value="false"/>
```

## What is never recorded

Anything that does not go through Guzzle: `file_get_contents()` on a URL, raw cURL, Symfony's HttpClient, and SDKs built on another transport. Requizon has no hook in those, and cannot see them.


## Common questions

### Does Requizon record calls made by SDKs that use their own Guzzle client?

Not on its own. An SDK that builds its own Guzzle client never passes through Laravel's HTTP client, so push BoringO11y\Requizon\Guzzle\RecordHttpRequests onto that client's handler stack. It is the same middleware the Laravel client uses, and a transfer that passes through it twice is still recorded once.

### How do I stop Requizon recording calls to my logging or metrics backend?

Add the host to ignore_hosts in config/requizon.php. Entries are Str::is() patterns such as *.datadoghq.com, and matching calls are not recorded at all. Without it, an exporter shipping telemetry over HTTP generates the very traffic it reports on.
