ddelosreyes/http-requests-logger-for-laravel

A simple HTTP request logger for Laravel with buffered inserts and support for both relational databases and DynamoDB.

Maintainers

Package info

github.com/delosreyesdan/http-requests-logger-for-laravel

pkg:composer/ddelosreyes/http-requests-logger-for-laravel

Statistics

Installs: 8

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.3 2026-05-10 15:16 UTC

This package is auto-updated.

Last update: 2026-05-10 15:18:39 UTC


README

Work in progress — API may change before v1.0.

Latest Version on Packagist

Logs incoming and outgoing HTTP requests without hammering your database. Every request is pushed to a Redis/Valkey list first, then flushed to storage in batches — keeping per-request DB writes entirely off the hot path.

Requirements

  • PHP 8.2+
  • Laravel 10, 11, or 12
  • ext-redis

Installation

composer require ddelosreyes/http-requests-logger-for-laravel
php artisan vendor:publish --provider="Ddelosreyes\HttpRequestsLogger\Providers\HttpRequestLoggerServiceProvider" --tag=config
php artisan vendor:publish --provider="Ddelosreyes\HttpRequestsLogger\Providers\HttpRequestLoggerServiceProvider" --tag=migrations
php artisan migrate

Usage

Register the middleware to log incoming requests:

// Laravel 11+ — bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\Ddelosreyes\HttpRequestsLogger\Middleware\HttpRequestLoggerMiddleware::class);
})

// Laravel 10 — app/Http/Kernel.php
protected $middleware = [
    \Ddelosreyes\HttpRequestsLogger\Middleware\HttpRequestLoggerMiddleware::class,
];

Each entry captures: direction, method, url, status, ip, user_agent, headers, body, duration_ms.

Log outgoing requests (Laravel Http:: facade) by setting:

HTTP_REQUEST_LOGGER_LOG_OUTGOING=true

Manually push a log from anywhere:

use Ddelosreyes\HttpRequestsLogger\Facades\HttpRequestLogger;

HttpRequestLogger::add([...]);

Or via the action directly:

use Ddelosreyes\HttpRequestsLogger\Actions\RequestLogBufferAction;

RequestLogBufferAction::add([...]);

Commands

php artisan logs:flush          # drain the buffer to storage
php artisan logs:clear          # discard the buffer without persisting
php artisan logs:status         # show buffer key, count, batch size, fill %
php artisan logs:prune --days=30  # delete database records older than N days

The buffer flushes automatically when it reaches batch_size. For low-traffic apps that never hit that threshold, enable the built-in scheduled drain:

HTTP_REQUEST_LOGGER_SCHEDULE=true
HTTP_REQUEST_LOGGER_SCHEDULE_CRON="* * * * *"

Or wire it yourself:

Schedule::command('logs:flush')->everyMinute()->withoutOverlapping();

Querying Logs

An Eloquent model is provided with query scopes for common filters:

use Ddelosreyes\HttpRequestsLogger\Models\HttpRequestLog;

HttpRequestLog::incoming()->get();
HttpRequestLog::outgoing()->get();
HttpRequestLog::failed()->get();                    // status >= 400
HttpRequestLog::successful()->get();                // status < 400
HttpRequestLog::slow(500)->get();                   // duration_ms > 500
HttpRequestLog::withStatus(422)->get();
HttpRequestLog::fromIp('1.2.3.4')->get();
HttpRequestLog::forUrl('/api/users')->get();
HttpRequestLog::within(60)->get();                  // last 60 minutes
HttpRequestLog::forUser(42)->get();                 // by user ID
HttpRequestLog::withRequestId('abc-123')->get();    // by correlation ID

Sensitive Data Masking

Fields listed under mask.headers and mask.body have their values replaced with *** before the log is stored. Header matching is case-insensitive. Body masking is recursive.

'mask' => [
    'headers' => ['Authorization', 'Cookie', 'X-Api-Key'],
    'body'    => ['password', 'token', 'secret', 'credit_card'],
],

Response Body Logging

Disabled by default to avoid storing large payloads. Enable and cap the size:

HTTP_REQUEST_LOGGER_LOG_RESPONSE_BODY=true
HTTP_REQUEST_LOGGER_MAX_BODY_SIZE=10240

Bodies larger than max_body_size bytes are stored truncated with a ...[truncated] suffix.

Storage Drivers

Set via HTTP_REQUEST_LOGGER_STORAGE. Default: database.

Driver Notes
database Any Laravel-supported DB (MySQL, PostgreSQL, SQLite). Single INSERT per batch.
dynamodb Uses batchWriteItem in chunks of 25 with one unprocessed-item retry.
redis Permanent Redis/Valkey list. Suitable for short-lived retention.
null Discards all logs. Useful for disabling without touching the middleware.

Configuration

return [
    // Master on/off switch — disable without removing middleware registration
    'enabled'     => env('HTTP_REQUEST_LOGGER_ENABLED', true),

    'storage'    => env('HTTP_REQUEST_LOGGER_STORAGE', 'database'),
    'batch_size' => env('HTTP_REQUEST_LOGGER_BATCH_SIZE', 500),

    // Log only a fraction of requests (0.0–1.0). 0.1 = ~10% of traffic.
    'sample_rate' => env('HTTP_REQUEST_LOGGER_SAMPLE_RATE', 1.0),

    'table'      => env('HTTP_REQUEST_LOGGER_TABLE', 'http_request_logs'),
    // Point logs at a dedicated DB connection instead of the default
    'connection' => env('HTTP_REQUEST_LOGGER_DB_CONNECTION', null),

    // Write directly to storage if Redis is unavailable instead of crashing
    'fallback_on_buffer_error' => env('HTTP_REQUEST_LOGGER_FALLBACK', true),

    // Log outgoing Http:: requests via the ResponseReceived event
    'log_outgoing' => env('HTTP_REQUEST_LOGGER_LOG_OUTGOING', false),

    // Capture auth()->id() on each log entry
    'log_user_id' => env('HTTP_REQUEST_LOGGER_LOG_USER_ID', false),

    // Header used to read/generate a request correlation ID
    'correlation_id_header' => env('HTTP_REQUEST_LOGGER_CORRELATION_ID_HEADER', 'X-Request-ID'),

    // Capture response bodies (both incoming and outgoing)
    'log_response_body' => env('HTTP_REQUEST_LOGGER_LOG_RESPONSE_BODY', false),
    'max_body_size'     => env('HTTP_REQUEST_LOGGER_MAX_BODY_SIZE', 10240),

    'mask' => [
        'headers' => [], // e.g. ['Authorization', 'Cookie']
        'body'    => [], // e.g. ['password', 'token']
    ],

    'exclude' => [
        'fields' => [], // e.g. ['headers', 'body']
        'paths'  => [], // e.g. ['/health', '/api/internal/*']
    ],

    'redis' => [
        'driver'   => env('HTTP_REQUEST_LOGGER_BUFFER_DRIVER', 'redis'), // "redis" or "valkey"
        'host'     => env('REDIS_HOST', '127.0.0.1'),
        'port'     => env('REDIS_PORT', 6379),
        'password' => env('REDIS_PASSWORD', null),
        'database' => env('REDIS_DB', 0),
        'prefix'   => env('HTTP_REQUEST_LOGGER_KEY_PREFIX', 'http_request_logs'),
    ],

    'redis_store' => [
        'max_entries' => env('HTTP_REQUEST_LOGGER_REDIS_MAX_ENTRIES', 10000),
    ],

    'dynamodb' => [
        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
    ],

    'schedule' => [
        'enabled' => env('HTTP_REQUEST_LOGGER_SCHEDULE', false),
        'cron'    => env('HTTP_REQUEST_LOGGER_SCHEDULE_CRON', '* * * * *'),
    ],
];

Testing

Requires a running Redis/Valkey instance. Use the included Docker setup:

make build
make test