farayaz/laravel-spy
Lightweight Laravel package that automatically logs all outgoing HTTP client requests. Capture URL, method, headers, payload, response headers/body, status code, and duration, with configurable logging and options to obfuscate sensitive data for debugging and auditing.
Installation:
composer require farayaz/laravel-spy
php artisan vendor:publish --provider="Farayaz\LaravelSpy\LaravelSpyServiceProvider"
php artisan migrate
Enable Logging:
Add to .env:
SPY_ENABLED=true
First Use Case:
$response = Http::get('https://api.example.com/data');
http_logs table and (if enabled) the /spy dashboard.config/spy.php (published via vendor:publish).http_logs (schema: id, url, method, headers, body, response_headers, response_body, status_code, duration_ms, created_at)./spy route (if SPY_DASHBOARD_ENABLED=true).php artisan spy:clean for log management.Debugging API Integrations:
.env and inspect logs for failed requests:
try {
Http::post('https://payment-gateway.com/charge', $payload);
} catch (\Exception $e) {
// Check logs for payload/response mismatches
}
status_code != 200.Compliance Auditing:
.env:
SPY_OBFUSCATES=password,api_key,credit_card
SPY_EXCLUDE_URLS=api/webhooks/stripe
CI/CD Validation:
# .github/workflows/test.yml
env:
SPY_ENABLED: true
$log = HttpLog::latest()->first();
$this->assertEquals('expected_payload', $log->body);
Performance Monitoring:
duration_ms:
$slowLogs = HttpLog::where('duration_ms', '>', 1000)->get();
Middleware Integration: Attach to specific routes to log internal requests (not just external HTTP calls):
Route::middleware([\Farayaz\LaravelSpy\Middleware\SpyMiddleware::class])->group(function () {
// Routes to log
});
Job/Queue Logging: Log async HTTP calls in jobs by enabling globally or per-job:
class PaymentJob implements ShouldQueue {
public function handle() {
// Automatically logged if SPY_ENABLED=true
Http::post('https://stripe.com/charge', $data);
}
}
Custom Storage:
Extend the HttpLog model to support alternative storage (e.g., Redis):
// app/Providers/AppServiceProvider.php
use Farayaz\LaravelSpy\Models\HttpLog;
public function boot() {
HttpLog::extend('redis', function ($model) {
return new RedisHttpLog($model);
});
}
Event-Based Extensions:
Listen to spy.logged events to process logs in real-time:
// app/Providers/EventServiceProvider.php
protected $listen = [
\Farayaz\LaravelSpy\Events\LogCreated::class => [
\App\Listeners\ProcessHttpLog::class,
],
];
Testing:
Mock logs in tests using the HttpLog facade:
HttpLog::shouldReceive('create')->once();
Performance Overhead:
SPY_FIELD_MAX_ROWS to limit log volume.spy:clean frequently to reduce table bloat.Binary Data Logging:
SPY_REQUEST_BODY_EXCLUDE_CONTENT_TYPES=image/,application/pdf
Middleware Conflicts:
SPY_DASHBOARD_MIDDLEWARE may clash with existing route middleware.routes/web.php:
Route::get('/spy', [\Farayaz\LaravelSpy\Http\Controllers\SpyController::class, 'index'])
->middleware(['web', 'auth', 'spy.dashboard']);
Obfuscation Edge Cases:
api_key_123 with SPY_OBFUSCATES=api_key).Dashboard Limitations:
HttpLog model:
$logs = HttpLog::where('status_code', '!=', 200)->paginate(20);
Future-Dated Release:
spatie/laravel-activitylog for critical projects.Log Missing?:
SPY_ENABLED=true and the request uses Laravel’s Http facade/client.SPY_EXCLUDE_URLS=api/health,ping
Corrupted Logs:
http_logs table:
php artisan migrate:fresh --table=http_logs
Dashboard Not Loading:
SPY_DASHBOARD_MIDDLEWARE and no route conflicts exist.Custom Log Fields:
Add columns to the http_logs table via a migration:
Schema::table('http_logs', function (Blueprint $table) {
$table->string('custom_metadata')->nullable();
});
Extend the HttpLog model to populate the field:
use Farayaz\LaravelSpy\Events\LogCreated;
LogCreated::listen(function ($log) {
$log->custom_metadata = json_encode(['user_id' => auth()->id()]);
$log->save();
});
Async Logging: Decouple logging from HTTP requests using queues:
// app/Providers/AppServiceProvider.php
use Farayaz\LaravelSpy\Jobs\LogHttpRequest;
public function boot() {
Http::macro('spy', function ($request) {
LogHttpRequest::dispatch($request);
return $request->send();
});
}
Slack/Email Alerts: Trigger alerts for failed requests:
// app/Providers/EventServiceProvider.php
protected $listen = [
\Farayaz\LaravelSpy\Events\LogCreated::class => [
\App\Listeners\AlertOnFailure::class,
],
];
GraphQL Support:
Extend to log GraphQL queries by intercepting Http::asForm() or custom clients:
Http::macro('graphql', function ($query, $variables = []) {
$response = Http::post('graphql-endpoint', [
'query' => $query,
'variables' => $variables,
]);
return $response;
});
Local Development Proxy:
Use SPY_ENABLED=true with Laravel Valet or Laravel Sail to inspect local API calls:
SPY_ENABLED=true SPY_EXCLUDE_URLS=localhost,127.0.0.1
How can I help you explore Laravel packages today?