mrnaeem4 / laravel-request-analysis
Laravel middleware that intercepts incoming HTTP requests and writes structured JSON request logs (JSONL) to the Laravel storage/logs directory with daily rotation and gzip compression.
Package info
github.com/mrnaeem4/laravel-request-analysis
pkg:composer/mrnaeem4/laravel-request-analysis
Requires
- php: ^8.2
- illuminate/contracts: ^12.0
- illuminate/http: ^12.0
- illuminate/support: ^12.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Laravel middleware that intercepts incoming HTTP requests, extracts structured
metadata (headers, body, files, source IP, etc.), and writes one JSON Line per
request directly to the storage/logs directory — with sensitive-field
redaction, body truncation, IP whitelisting, and daily log rotation with gzip
compression.
Local logging only. No external server, no queue, no Guzzle. This is the Laravel counterpart of ci4-request-analysis. Laravel 12+.
Features
- Attachable per-route via the
requestlogmiddleware alias. - Writes directly to
storage/logs/analysis.logas JSON Lines (JSONL). - Daily rotation: a file from a previous day is renamed and gzip-compressed automatically on the next write.
- Retention pruning: compressed logs older than
REQUEST_LOG_RETENTION_DAYS(default 30) are deleted. - Configurable sensitive-field redaction (default:
password,nik,Api-Key,no_telp). - Raw body truncation at 3 MB (configurable) with
... [truncated]suffix. - File upload metadata captured without binary content (name, size, MIME, extension, SHA-256 hash, double-extension detection).
- IP/CIDR whitelist to skip private/internal traffic.
- PHP 8.2+.
- Auto-discovery via
composer.jsonextra.laravel.providers.
Requirements
- PHP 8.2+
- Laravel 12.x
Installation
composer require mrnaeem4/laravel-request-analysis
1. Publish the config (optional)
php artisan vendor:publish --tag=request-log-config
This copies config/request-log.php to your application config directory so
you can override defaults.
2. Register middleware alias
The service provider already registers the requestlog alias. For Laravel
11/12+, make sure bootstrap/app.php includes the middleware alias (auto-
discovery should handle it, but verify):
->withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'requestlog' => \MrNaeem\LaravelRequestAnalysis\Http\Middleware\RequestLogMiddleware::class, ]); })
3. Attach to routes
Route::middleware('requestlog')->group(function () { Route::post('post', [HomeController::class, 'post']); Route::post('upload', [HomeController::class, 'upload']); });
4. Set environment variables
REQUEST_LOG_ENABLED = true REQUEST_LOG_LOG_DIR = "" REQUEST_LOG_LOG_FILE = "analysis.log" REQUEST_LOG_REDACT_FIELDS = "password,nik,Api-Key,no_telp" REQUEST_LOG_MAX_BODY_SIZE = 3145728 REQUEST_LOG_WHITELIST_IPS = "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1" REQUEST_LOG_TRUNCATE_SUFFIX = "... [truncated]" REQUEST_LOG_RETENTION_DAYS = 30
Config reference
| Variable | Default | Description |
|---|---|---|
REQUEST_LOG_ENABLED |
false |
Master switch. |
REQUEST_LOG_LOG_DIR |
"" (→ storage/logs) |
Directory for the log file. |
REQUEST_LOG_LOG_FILE |
analysis.log |
Log file name (daily rotated). |
REQUEST_LOG_REDACT_FIELDS |
password,nik,Api-Key,no_telp |
Comma-separated sensitive fields. |
REQUEST_LOG_MAX_BODY_SIZE |
3145728 (3 MB) |
Body truncation length (bytes). |
REQUEST_LOG_WHITELIST_IPS |
RFC1918 + localhost | CIDR ranges to skip. |
REQUEST_LOG_TRUNCATE_SUFFIX |
... [truncated] |
Truncation suffix. |
REQUEST_LOG_RETENTION_DAYS |
30 |
Days of compressed logs kept. |
Artisan command
php artisan requestlog:rotate
Rotation happens automatically on write. Run this manually or via cron as a guaranteed nightly pass.
Log payload
Each line in storage/logs/analysis.log is a JSON object:
{
"log_data": {
"timestamp": "2026-08-31T02:15:04+00:00",
"domain": "app.example.com",
"path": "/api/profile/update",
"method": "POST",
"srcip": "203.0.113.10",
"user_agent": "Mozilla/5.0 ...",
"query_string": "page=1",
"headers": { "Content-Type": "application/json", ... },
"raw_body": "{\"name\":\"User\",\"email\":\"user@example.com\",\"password\":\"***REDACTED***\"}",
"file_count": 1,
"file_names": ["shell.php.jpg"],
"file_metadata": [
{
"original_name": "shell.php.jpg",
"size": 20480,
"mime_type": "image/jpeg",
"extension": "jpg",
"hash": "3c98...",
"has_double_extension": true
}
]
},
"retry_count": 0,
"last_attempt": null,
"created_at": "2026-08-31T02:15:04+00:00"
}
How it works
Request → RequestLogMiddleware (handle)
├─ enabled? ──no──► done
├─ IP whitelisted? ──yes──► done
├─ collect: headers, body, files, srcip...
│ ├─ redact sensitive fields (headers + body)
│ ├─ truncate body at max size
│ └─ extract file metadata (no binary)
├─ rotate if the active log is from a previous day (rename + gzip + prune)
└─ append one JSONL line to storage/logs/analysis.log
Security
storage/logs/analysis.log contains request headers and bodies. Ensure the
web server does not serve the storage/ directory — Laravel already blocks
this by default. For extra protection, set REQUEST_LOG_LOG_DIR to a path
outside the document root.
Sample app
Minimal sample code under sample/:
sample/routes/web.php— route group withrequestlogmiddlewaresample/app/Http/Controllers/HomeController.php—post+uploaddemo
Changelog
See CHANGELOG.md.