andriichuk / laravel-http-client-logger
Configurable request/response logger for Laravel HTTP client with sanitization and status filters
Package info
github.com/andriichuk/laravel-http-client-logger
pkg:composer/andriichuk/laravel-http-client-logger
Fund package maintenance!
Requires
- php: ^8.3
- guzzlehttp/guzzle: ^7.0
- illuminate/http: ^11.0||^12.0
- illuminate/support: ^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
README
A super simple, configurable logger for Laravel’s HTTP client (outgoing requests). Ideal for APIs and third-party integrations.
- Sanitization — Mask sensitive fields and headers (e.g. password, authorization).
- Filters — Limit by response status (2xx, 4xx, 5xx, etc.).
- Headers — Choose which request/response headers to include in logs. Use
['*']for all; when the arrays are empty, no header keys are added to the log at all. - Optional response body — Include decoded, sanitized response body (JSON); non-JSON can be truncated or skipped.
- Request files (multipart) — When sending files via
attach(), log metadata (name, original_name, size, mime_type, extension) in context asuploaded_files; no file contents are logged. Aligns with laravel-http-logger.
Installation
Requirements: PHP 8.2+ and Laravel 10.x, 11.x or 12.x.
Install the package via Composer:
composer require andriichuk/laravel-http-client-logger
Publish the config file:
php artisan vendor:publish --tag="http-client-logger-config"
(Optional) Add a dedicated log channel for HTTP client logs in config/logging.php (e.g. a separate file or stack). If you skip this, the package uses your default log channel.
'channels' => [ // ... 'http_client' => [ 'driver' => 'daily', 'path' => storage_path('logs/http_client.log'), 'level' => 'debug', ], ],
Important — when is logging on? If
LOG_HTTP_CLIENT_REQUESTSis not set in.env, the logger followsAPP_DEBUG: it is enabled whenAPP_DEBUG=true(e.g. local) and disabled whenAPP_DEBUG=false(e.g. production). To force it on or off, setLOG_HTTP_CLIENT_REQUESTS=trueorLOG_HTTP_CLIENT_REQUESTS=falsein your.env, or set'enabled'inconfig/http-client-logger.php. The log channel can be overridden withHTTP_CLIENT_LOG_CHANNEL(e.g.HTTP_CLIENT_LOG_CHANNEL=http_clientto use the channel above).
Configuration
After publishing, configure config/http-client-logger.php as needed.
| Key | Description | Default |
|---|---|---|
enabled |
Master switch for HTTP client logging. When LOG_HTTP_CLIENT_REQUESTS is unset, falls back to APP_DEBUG. When disabled, the log macro still exists but no entries are written. |
env('LOG_HTTP_CLIENT_REQUESTS', APP_DEBUG) |
channel |
Log channel name (must exist in config/logging.php). |
HTTP_CLIENT_LOG_CHANNEL or LOG_CHANNEL or 'stack' |
report |
Which response status categories to log: info (1xx), success (2xx), redirect (3xx), client_error (4xx), server_error (5xx). Each key is a boolean. |
info/success → false; redirect/client_error/server_error → true |
log_level_by_status |
Map each status category to a PSR log level (debug, info, notice, warning, error, etc.). 5xx → error and 4xx → warning by default for easier filtering. |
client_error → warning; server_error → error; others → info |
include_response |
Include response body in log context. | true |
include_non_json_response |
When include_response is true, include non-JSON bodies (HTML, text, etc.) in the log (truncated). Set to true to include them; default logs as '[skipped]'. |
false |
include_request_headers |
Request header names (lowercase) to include. Use ['*'] for all. When empty, no request headers are added to the log. |
['*'] |
include_response_headers |
Response header names (lowercase) to include. Use ['*'] for all. When empty, no response headers are added to the log. |
['*'] |
include_uploaded_files_metadata |
When true, multipart requests that send files (e.g. via attach()) include file metadata in log context as uploaded_files: name, original_name, size, mime_type, extension, error. No file contents are logged. |
true |
sensitive_fields |
Request/response body keys to replace with ***. |
['token', 'refresh_token', 'password', …] |
sensitive_headers |
Header names (lowercase) to replace with ***. |
['authorization', 'cookie'] |
max_string_value_length |
Max length for string values in bodies (and non-JSON response body) before truncation. Use null to disable truncation. |
100 |
message_prefix |
Prefix for the log message. | '[HttpClientLogger] ' |
Response body logging: JSON responses are decoded and sanitized; max_string_value_length applies to each string value. Non-JSON responses are logged as a truncated string or '[skipped]'. The log level follows response status by default (5xx → error, 4xx → warning, 1xx/2xx/3xx → info); configure via log_level_by_status.
Usage
Use the log macro on the Laravel HTTP client to log that request and its response (or failure).
Basic:
use Illuminate\Support\Facades\Http; Http::log()->get('https://api.example.com/users'); Http::log()->post('https://api.example.com/orders', ['item' => 'widget']);
With context (e.g. name in the log message):
Http::log(['name' => 'Stripe'])->post('https://api.stripe.com/v1/charges', $payload);
Using the name macro:
Use the name macro to add a name key to the logging context and include it in the log message. Handy for chaining and identifying which integration a request belongs to.
Http::name('Stripe')->log()->get('https://api.stripe.com/v1/balance');
The name appears in the log message right after the prefix (e.g. [HttpClientLogger] Stripe GET https://api.stripe.com/v1/balance).
Sending files (multipart):
When you send files with attach(), the logger can include file metadata in the log context as uploaded_files (name, original_name, size, mime_type, extension). No file contents are logged. Enable or disable via include_uploaded_files_metadata in config.
Http::log()->attach('document', $fileContents, 'report.pdf')->post('https://api.example.com/upload'); // or with Laravel's UploadedFile: Http::log()->attach('avatar', $request->file('avatar'))->post('https://api.example.com/profile/photo');
When enabled is false in config, the macro is still available but the middleware does not write any log entries.
Example log output
Successful API call (200): INFO level, with name and sanitized response.
[2026-03-14 10:15:22] local.INFO: [HttpClientLogger] Stripe GET https://api.stripe.com/v1/balance {"response_status_code":200,"request_headers":{"content-type":["application/json"],"authorization":"***"},"response_headers":{"content-type":["application/json"]},"request_body":[],"response_body":{"available":[{"amount":1000,"currency":"usd"}],"token":"***"},"execution_time_ms":142}
Client error (422): WARNING level, authorization masked, JSON response with validation errors.
[2026-03-14 10:16:01] local.WARNING: [HttpClientLogger] POST https://api.example.com/orders {"response_status_code":422,"request_headers":{"content-type":["application/json"],"authorization":"***"},"response_headers":[],"request_body":{"item":"widget","quantity":-1},"response_body":{"message":"The quantity must be at least 1.","errors":{"quantity":["The quantity must be at least 1."]}},"execution_time_ms":89}
Message (no name): [HttpClientLogger] GET https://api.example.com/users
Multipart with file: uploaded_files in context (metadata only; no file contents).
[2026-03-14 10:20:00] local.INFO: [HttpClientLogger] POST https://api.example.com/upload {"response_status_code":200,"request_headers":{...},"response_headers":{},"request_body":"...","response_body":"...","execution_time_ms":45,"uploaded_files":[{"name":"document","original_name":"report.pdf","size":null,"mime_type":null,"extension":"pdf","error":0}]}
Testing
Run the test suite with Pest:
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
License
The MIT License (MIT). Please see License File for more information.