bilfeldt/laravel-http-client-logger
Log outgoing HTTP requests and responses made with Laravel’s HTTP client. The package adds configurable middleware that captures URLs, status codes, timing, headers, and payloads, with sensitive-data redaction and flexible logging options.
bilfeldt/laravel-http-client-logger adds a middleware to Laravel's HTTP client that logs every outgoing request and response. It's a drop-in observability layer for the Http facade — useful the moment you have integrations (payment providers, CRMs, internal APIs) and no easy way to see what was actually sent.
Install and publish config:
composer require bilfeldt/laravel-http-client-logger
php artisan vendor:publish --tag=http-client-logger
Register the middleware globally in AppServiceProvider::boot():
use Bilfeldt\HttpClientLogger\Http\Middleware\LogRequestMiddleware;
use Illuminate\Support\Facades\Http;
Http::globalMiddleware(LogRequestMiddleware::class);
That's it — the next outgoing call lands in your default log channel. Check config/http-client-logger.php first; the main knobs are:
logger — the logger implementation class (defaults to the bundled LaravelLogger).log_profile — full, short, headers, or none. Start with short while exploring; full dumps entire bodies.log_channel — pin logs to a dedicated channel instead of polluting laravel.log.First use case: point it at a flaky integration, set log_profile to full in local, fire a job, and read the request/response pair back out of storage/logs/laravel.log. This is the fastest way to answer "did we send the right payload and what did they actually return" without a debugger or a proxy like Charles.
If you've been on v2.5.x, v2.6.0 is a pure compatibility bump (Laravel 13.x support) with no API or config changes — a safe, no-op upgrade.
Scope it to a single client. Global middleware logs everything, including health checks and third-party noise. For a per-service setup, build a dedicated client and attach middleware there:
$client = Http::withMiddleware(LogRequestMiddleware::class)
->baseUrl('https://api.stripe.com');
Toggle by environment, not by code. Keep the middleware registered everywhere and drive it from config so you can flip it on in staging without a redeploy:
// config/http-client-logger.php
'log_profile' => env('HTTP_CLIENT_LOG_PROFILE', 'short'),
Then set HTTP_CLIENT_LOG_PROFILE=none in production unless you have a dedicated, redacted channel.
Dedicated log channel. Create an http-client channel in config/logging.php and point log_channel at it. This lets you apply a separate retention policy, route it to a central collector, or tail it in isolation with php artisan tail-style tooling. It also keeps noisy payloads out of the main app log, where they drown out real errors.
Custom logger for redaction and structure. The logger config value is resolved from the container, so swapping it is the documented extension point. Subclass LaravelLogger (or implement the package's logger contract) when you need to strip authorization headers, customer emails, or card fragments before the record is written:
class RedactingLogger extends \Bilfeldt\HttpClientLogger\Loggers\LaravelLogger
{
// override the request/response serialisation to mask fields
}
Register it via 'logger' => App\Logging\RedactingLogger::class — no middleware changes needed.
Add correlation context. Because the middleware runs inside the request/job lifecycle, you can combine it with Laravel's logging context to attach a trace or job ID. That's what makes the logs usable in a queue worker handling hundreds of jobs — without a correlation ID, interleaved request/response pairs are unreadable.
Pair it with Http::fake() in tests deliberately. Faked responses still flow through the client pipeline, so logging will fire in your test suite. Either set log_profile to none in phpunit.xml, or use it as a cheap assertion surface for what your code was about to send.
full is a data-leak waiting to happen. It logs complete request and response bodies, including bearer tokens, API keys, and customer PII in query strings, headers, and payloads. Never ship full as the production default — use short or headers, and pair it with a custom logger that redacts known-sensitive keys.full profile against an endpoint returning a large JSON or binary blob produces enormous entries, slows the request path, and can blow through log-storage quotas on high-traffic queue workers. Consider disabling on chatty clients.ConnectionException before a response exists, confirm whether you get a log line. If not, wrap critical calls or add a try/catch that logs the outbound attempt yourself, otherwise the most important failures are exactly the ones missing from your logs.log_channel of null falls back to the default stack — which in many apps means a daily file, mixed with everything else. Set it explicitly; this is the single highest-leverage config tweak for making the package actually useful.config/http-client-logger.php, run php artisan config:clear (or config:cache in deploy) — otherwise your log_profile change silently does nothing in a cached environment. Same applies to env-driven overrides.^2.6 and move on.How can I help you explore Laravel packages today?