Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Laravel HTTP Logger
https://github.com/spatie/laravel-http-logger
Admin
A Laravel package that provides a middleware to log incoming HTTP requests, acting as an extra
...
Tokens:
5,389
Snippets:
23
Trust Score:
8.5
Update:
5 months ago
Context
Skills
Chat
Benchmark
78.2
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Laravel HTTP Logger Laravel HTTP Logger is a middleware package for Laravel applications that automatically logs incoming HTTP requests to your application's log system. This package provides a safety net for critical user submissions by preserving request data even when errors occur during processing. The middleware intercepts requests, applies configurable filtering rules, sanitizes sensitive data, and writes structured log entries containing the request method, URI, body, headers, and uploaded files. The package uses a strategy pattern with two main components: a LogProfile that determines which requests should be logged, and a LogWriter that handles the actual logging logic. Out of the box, it logs all POST, PUT, PATCH, and DELETE requests while excluding sensitive fields like passwords. The package is highly extensible, allowing developers to implement custom filtering logic and logging strategies to meet their specific security and debugging requirements. ## Installation and Setup Install the package via Composer and optionally publish the configuration file. ```bash # Install via Composer composer require spatie/laravel-http-logger # Publish configuration file (optional) php artisan vendor:publish --provider="Spatie\HttpLogger\HttpLoggerServiceProvider" --tag="config" ``` ## Middleware Registration (Laravel 11+) Register the middleware globally to log all incoming requests. ```php // bootstrap/app.php use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // Add HTTP logger middleware globally $middleware->append(\Spatie\HttpLogger\Middlewares\HttpLogger::class); }) ->withExceptions(function (Exceptions $exceptions) { // })->create(); ``` ## Middleware Registration (Laravel 10 and below) Register the middleware in the HTTP Kernel for older Laravel versions. ```php // app/Http/Kernel.php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { protected $middleware = [ \App\Http\Middleware\TrustProxies::class, \Illuminate\Http\Middleware\HandleCors::class, \App\Http\Middleware\PreventRequestsDuringMaintenance::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, // Add HTTP logger middleware \Spatie\HttpLogger\Middlewares\HttpLogger::class, ]; } ``` ## Route-Specific Middleware Apply the middleware to specific routes only. ```php // routes/web.php use Illuminate\Support\Facades\Route; // Apply middleware to a single route Route::post('/submit-form', function (Illuminate\Http\Request $request) { // Process form submission $data = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email', 'message' => 'required|string', ]); // Handle the submission return response()->json(['success' => true]); })->middleware(\Spatie\HttpLogger\Middlewares\HttpLogger::class); // Apply middleware to a route group Route::middleware([\Spatie\HttpLogger\Middlewares\HttpLogger::class])->group(function () { Route::post('/orders', [OrderController::class, 'store']); Route::put('/orders/{order}', [OrderController::class, 'update']); Route::delete('/orders/{order}', [OrderController::class, 'destroy']); }); ``` ## Basic Configuration Configure which requests to log and which fields to exclude. ```php // config/http-logger.php return [ // Enable/disable logging via environment variable 'enabled' => env('HTTP_LOGGER_ENABLED', true), // Log profile determines which requests to log 'log_profile' => \Spatie\HttpLogger\LogNonGetRequests::class, // Log writer handles the actual logging 'log_writer' => \Spatie\HttpLogger\DefaultLogWriter::class, // Laravel log channel to use 'log_channel' => env('LOG_CHANNEL', 'stack'), // Log level (debug, info, notice, warning, error, critical, alert, emergency) 'log_level' => 'info', // Fields to exclude from request body logging 'except' => [ 'password', 'password_confirmation', 'credit_card', 'cvv', ], // Headers to sanitize (replaced with ****) 'sanitize_headers' => [ 'Authorization', 'Cookie', 'Set-Cookie', ], ]; // .env file HTTP_LOGGER_ENABLED=true LOG_CHANNEL=daily ``` ## Default Log Output The default log writer produces structured log entries with method, URI, body, headers, and files. ```php // Sample POST request to /api/users // POST http://example.com/api/users // Headers: Authorization: Bearer token123, Content-Type: application/json // Body: {"name": "John Doe", "email": "john@example.com", "password": "secret"} // Resulting log entry (with default configuration): // [2025-10-10 15:23:45] local.INFO: POST /api/users - Body: {"name":"John Doe","email":"john@example.com"} - Headers: {"authorization":["****"],"content-type":["application\/json"],"accept":["*\/*"]} - Files: // The password field is excluded (per 'except' config) // The Authorization header is sanitized (per 'sanitize_headers' config) ``` ## Custom Log Profile Implement custom filtering logic to determine which requests should be logged. ```php // app/Logging/CustomLogProfile.php namespace App\Logging; use Illuminate\Http\Request; use Spatie\HttpLogger\LogProfile; class CustomLogProfile implements LogProfile { public function shouldLogRequest(Request $request): bool { // Don't log if logging is disabled if (! config('http-logger.enabled')) { return false; } // Only log requests to specific paths if (str_starts_with($request->path(), 'api/')) { return true; } // Log all non-GET requests to admin routes if (str_starts_with($request->path(), 'admin/') && ! $request->isMethod('GET')) { return true; } // Log any request that contains a specific header if ($request->hasHeader('X-Debug-Request')) { return true; } return false; } } // config/http-logger.php return [ 'log_profile' => \App\Logging\CustomLogProfile::class, // ... other config ]; ``` ## Log Only Failed Requests Create a log profile that only logs requests that result in errors. ```php // app/Logging/LogFailedRequestsProfile.php namespace App\Logging; use Illuminate\Http\Request; use Spatie\HttpLogger\LogProfile; class LogFailedRequestsProfile implements LogProfile { protected static $shouldLog = []; public function shouldLogRequest(Request $request): bool { if (! config('http-logger.enabled')) { return false; } // Generate a unique ID for this request $requestId = spl_object_hash($request); // Store the request ID to check later in exception handler static::$shouldLog[$requestId] = true; // Register a terminating callback to remove successful requests app()->terminating(function () use ($requestId) { unset(static::$shouldLog[$requestId]); }); return in_array(strtolower($request->method()), ['post', 'put', 'patch', 'delete']); } } // Note: This example shows the pattern, but in production you'd coordinate // with Laravel's exception handler for more reliable failure detection ``` ## Custom Log Writer with JSON Format Implement a custom log writer that outputs JSON-formatted logs. ```php // app/Logging/JsonLogWriter.php namespace App\Logging; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Spatie\HttpLogger\LogWriter; use Spatie\HttpLogger\Sanitizer; class JsonLogWriter implements LogWriter { public function logRequest(Request $request) { $sanitizer = new Sanitizer(); $logData = [ 'timestamp' => now()->toIso8601String(), 'request_id' => $request->id(), 'method' => $request->method(), 'url' => $request->fullUrl(), 'path' => $request->path(), 'ip' => $request->ip(), 'user_agent' => $request->userAgent(), 'user_id' => auth()->id(), 'body' => $request->except(config('http-logger.except')), 'query' => $request->query(), 'headers' => $sanitizer->clean( $request->headers->all(), config('http-logger.sanitize_headers') ), 'files' => array_keys($request->allFiles()), ]; Log::channel(config('http-logger.log_channel')) ->log( config('http-logger.log_level', 'info'), 'HTTP Request', $logData ); } } // config/http-logger.php return [ 'log_writer' => \App\Logging\JsonLogWriter::class, // ... other config ]; // Example log output: // [2025-10-10 15:23:45] local.INFO: HTTP Request {"timestamp":"2025-10-10T15:23:45+00:00","request_id":"abc123","method":"POST","url":"http://example.com/api/users?source=web","path":"api/users","ip":"192.168.1.1","user_agent":"Mozilla/5.0","user_id":42,"body":{"name":"John Doe","email":"john@example.com"},"query":{"source":"web"},"headers":{"authorization":["****"]},"files":["avatar"]} ``` ## Custom Log Writer with Database Storage Store HTTP request logs in a database for querying and analysis. ```php // Create migration // php artisan make:migration create_http_logs_table // database/migrations/xxxx_xx_xx_create_http_logs_table.php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::create('http_logs', function (Blueprint $table) { $table->id(); $table->string('method', 10); $table->string('path'); $table->json('body')->nullable(); $table->json('headers')->nullable(); $table->json('files')->nullable(); $table->ipAddress('ip_address')->nullable(); $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete(); $table->timestamps(); $table->index(['method', 'path']); $table->index('created_at'); }); } public function down() { Schema::dropIfExists('http_logs'); } }; // app/Models/HttpLog.php namespace App\Models; use Illuminate\Database\Eloquent\Model; class HttpLog extends Model { protected $fillable = [ 'method', 'path', 'body', 'headers', 'files', 'ip_address', 'user_id', ]; protected $casts = [ 'body' => 'array', 'headers' => 'array', 'files' => 'array', ]; public function user() { return $this->belongsTo(User::class); } } // app/Logging/DatabaseLogWriter.php namespace App\Logging; use App\Models\HttpLog; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Spatie\HttpLogger\LogWriter; use Spatie\HttpLogger\Sanitizer; use Symfony\Component\HttpFoundation\File\UploadedFile; class DatabaseLogWriter implements LogWriter { public function logRequest(Request $request) { $sanitizer = new Sanitizer(); $files = (new Collection(iterator_to_array($request->files))) ->map(function ($file) { return $this->flatFiles($file); }) ->flatten() ->toArray(); HttpLog::create([ 'method' => $request->method(), 'path' => $request->path(), 'body' => $request->except(config('http-logger.except')), 'headers' => $sanitizer->clean( $request->headers->all(), config('http-logger.sanitize_headers') ), 'files' => $files, 'ip_address' => $request->ip(), 'user_id' => auth()->id(), ]); } protected function flatFiles($file) { if ($file instanceof UploadedFile) { return $file->getClientOriginalName(); } if (is_array($file)) { return array_map([$this, 'flatFiles'], $file); } return (string) $file; } } // config/http-logger.php return [ 'log_writer' => \App\Logging\DatabaseLogWriter::class, // ... other config ]; // Query the logs $recentPosts = HttpLog::where('method', 'POST') ->where('created_at', '>=', now()->subHours(24)) ->get(); $userActivity = HttpLog::where('user_id', 123) ->orderBy('created_at', 'desc') ->take(50) ->get(); ``` ## Sanitizer API Use the Sanitizer class to mask sensitive data in arrays. ```php use Spatie\HttpLogger\Sanitizer; $sanitizer = new Sanitizer(); // Basic usage with default mask (****) $input = [ 'username' => 'john', 'password' => 'secret123', 'api_key' => 'key_abc123', ]; $cleaned = $sanitizer->clean($input, ['password', 'api_key']); // Result: ['username' => 'john', 'password' => '****', 'api_key' => '****'] // Custom mask $sanitizer->setMask('[REDACTED]'); $cleaned = $sanitizer->clean($input, ['password']); // Result: ['username' => 'john', 'password' => '[REDACTED]', 'api_key' => 'key_abc123'] // Nested arrays $nested = [ 'user' => [ 'name' => 'John', 'credentials' => [ 'username' => 'john', 'password' => 'secret', ], ], 'password' => 'another_secret', ]; $cleaned = $sanitizer->clean($nested, ['password']); // Result: // [ // 'user' => [ // 'name' => 'John', // 'credentials' => [ // 'username' => 'john', // 'password' => '****', // ], // ], // 'password' => '****', // ] // Case-insensitive matching $input = [ 'Authorization' => 'Bearer token123', 'authorization' => 'Basic xyz', 'AUTHORIZATION' => 'Bearer abc', ]; $cleaned = $sanitizer->clean($input, ['authorization']); // All variations are sanitized: ['Authorization' => '****', 'authorization' => '****', 'AUTHORIZATION' => '****'] ``` ## Dynamic Configuration Based on Environment Adjust logging behavior based on the application environment. ```php // config/http-logger.php return [ 'enabled' => env('HTTP_LOGGER_ENABLED', app()->environment('production')), 'log_profile' => app()->environment('production') ? \Spatie\HttpLogger\LogNonGetRequests::class : \App\Logging\LogAllRequestsProfile::class, 'log_writer' => app()->environment('production') ? \App\Logging\DatabaseLogWriter::class : \Spatie\HttpLogger\DefaultLogWriter::class, 'log_channel' => app()->environment('production') ? 'daily' : 'single', 'log_level' => app()->environment('production') ? 'warning' : 'debug', 'except' => array_merge( ['password', 'password_confirmation'], app()->environment('production') ? ['ssn', 'credit_card'] : [] ), 'sanitize_headers' => app()->environment('production') ? ['Authorization', 'Cookie', 'Set-Cookie', 'X-Api-Key'] : [], ]; ``` ## Testing with HTTP Logger Verify that requests are being logged correctly in your tests. ```php // tests/Feature/HttpLoggerTest.php namespace Tests\Feature; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Log; use Tests\TestCase; class HttpLoggerTest extends TestCase { use RefreshDatabase; public function test_post_requests_are_logged() { Log::spy(); $this->post('/api/users', [ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'secret123', ]); Log::shouldHaveReceived('channel') ->with(config('http-logger.log_channel')) ->once(); } public function test_get_requests_are_not_logged() { Log::spy(); $this->get('/api/users'); Log::shouldNotHaveReceived('channel'); } public function test_sensitive_fields_are_excluded() { Log::shouldReceive('channel') ->andReturnSelf(); Log::shouldReceive('log') ->once() ->withArgs(function ($level, $message) { return ! str_contains($message, 'secret123'); }); $this->post('/api/users', [ 'name' => 'John Doe', 'password' => 'secret123', ]); } } ``` ## Integration with External Logging Services Send HTTP request logs to external services like Sentry or LogRocket. ```php // app/Logging/SentryLogWriter.php namespace App\Logging; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Spatie\HttpLogger\LogWriter; use Spatie\HttpLogger\Sanitizer; class SentryLogWriter implements LogWriter { public function logRequest(Request $request) { $sanitizer = new Sanitizer(); // Log to default Laravel log $logData = [ 'method' => $request->method(), 'url' => $request->fullUrl(), 'body' => $request->except(config('http-logger.except')), 'headers' => $sanitizer->clean( $request->headers->all(), config('http-logger.sanitize_headers') ), ]; Log::channel(config('http-logger.log_channel')) ->info('HTTP Request', $logData); // Also send to Sentry as breadcrumb if (function_exists('sentry_get_current_hub')) { \Sentry\addBreadcrumb( category: 'http.request', message: "{$request->method()} {$request->path()}", metadata: [ 'url' => $request->fullUrl(), 'method' => $request->method(), 'ip' => $request->ip(), 'user_id' => auth()->id(), ], level: \Sentry\Breadcrumb::LEVEL_INFO ); } } } ``` ## Summary Laravel HTTP Logger provides a robust middleware solution for capturing and persisting HTTP request data in Laravel applications. The package excels at debugging production issues, auditing user actions, and maintaining a safety net for critical form submissions. Its default configuration intelligently logs mutation requests (POST, PUT, PATCH, DELETE) while excluding sensitive fields and sanitizing authentication headers, making it secure and practical out of the box. The package's extensibility through the LogProfile and LogWriter interfaces enables diverse use cases: logging only failed requests, storing logs in databases for analysis, integrating with external monitoring services, or implementing custom filtering logic based on user roles or request paths. Whether you need simple file-based logging for debugging or sophisticated database-driven audit trails for compliance, Laravel HTTP Logger's architecture adapts to your requirements while maintaining a minimal footprint on application performance.