When a page gets slow, the cause is often a call your app made to somebody else's API. Your APM shows the request took four seconds. It rarely shows that 3.8 of them went to one vendor's search endpoint, or that the vendor has been timing out since nine this morning.
httptheus puts those calls in Prometheus. It hooks Laravel's HTTP client globally, so every
Http::get() and Http::pool() is recorded without touching the code that makes them.
The same middleware can be pushed onto a raw Guzzle handler stack, which covers the SDKs that build their own
client.
Two metrics, and what each one answers
httptheus_client_request_duration_seconds is a histogram labelled by host, method, endpoint and
status class. It answers "how slow is Stripe at p95" and "how many requests a minute go to this API", from the
same series:
histogram_quantile(0.95, sum by (le) (
rate(httptheus_client_request_duration_seconds_bucket{host="api.stripe.com"}[5m])
))
httptheus_client_request_errors_total counts only the transfers that never got a response, with
a reason of timeout, DNS, connection refused, TLS or network. A 500 already has a status class. What
a status cannot tell you is whether the other side is down or your DNS is broken, and that is what this counter
is for.
Bodies and headers are never read, and nothing is written to your database. A Grafana dashboard for both metrics ships with the package and imports onto any Prometheus datasource.
Endpoints without a series explosion
A label per URL path would create a series for every user id you ever called. httptheus normalises paths before
they become labels: numeric ids, UUIDs, ULIDs and long segments become :id, and only the first three
segments are kept. So /v1/users/8134/orders/99 is recorded as /v1/users/:id/*. If
that heuristic is not enough for an API you call, an allow-list of patterns puts everything else into a single
other series. The dashboard has a panel counting distinct endpoint labels, so you see growth before
Prometheus does.
Where the numbers are kept between requests
Under PHP-FPM each request is a fresh process, so counters need shared storage to survive until the scrape.
httptheus uses APCu when it is available and can use Redis, which is what queue workers need because APCu is
per process. If you already run spatie/laravel-prometheus,
the metrics appear on its /prometheus endpoint with no configuration.
Otherwise httptheus serves its own scrape at /httptheus/metrics. A scrape lists every host your
app talks to, so until you allow an IP range or add a gate it answers only in the local
environment.
Two things to know
-
It counts transfers, not calls. A redirect chain, or a request retried with Laravel's
retry(), records every hop, so the count can be higher than the number of calls your code made. - Aggregates only. When you need the individual request that failed, with its response body, that is a different tool.
Every configuration key, and how to run the test suite, is in the README on GitHub. Bugs and questions go in its issues.