Access Log Middleware

After you require the package. When you do php artisan migrate you will notice a new migration/table getting migrated.

It is the create_access_logs_table migration.

That is because the AccessLogMiddleware saves logged api endpoints in this table.

Usage

To use the AccessLogMiddleware, all you have to do is wrap the middleware access.log around your desired endpoints like below

// api.php

Route::middleware(['access.log'])->group(function () {
    Route::get('/hello', function () {
        return 'Hello World';
    });
});

Result

After you wrapped the access.log middleware around your api endpoints.

Now, if you hit that endpoint yourUrl:8000/hello it will return the string Hello World.

If you go to your access log table now you will see that the endpoint information were saved to the access log database table.



Access Log Result Image



It has information like:

  1. The Ip Address
  2. The Method Type (Get, Post, Put, Delete, Options, and Etc...)
  3. The Endpoint Url
  4. The User Agent
  5. The Request Data (If it a Post/Put Request)
  6. The Timestamps (Created At, and Updated At)

Redaction

Request bodies are recorded, so credentials must not be. Anything matching the exclusion list is dropped from both the stored body and the query string before the row is written:

password, password_confirmation, current_password,
new_password, new_password_confirmation,
remember_token, api_token, access_token, refresh_token,
api_key, client_secret, secret, token

current_password matters in particular: it is the field Laravel Breeze's password-update form submits, so without it a password-change route logged the user's existing password in plaintext.

A login request therefore records who tried to sign in without recording what they typed:

{"email": "user@example.com"}

Matching is case-insensitive and applies at any depth, so a nested user[password] is redacted by the bare password entry:

{"user": {"email": "user@example.com"}}

An entry written in dot notation targets one specific nested key instead, leaving a same-named key elsewhere alone:

'excluded_attributes' => ['user.pin'],

The list is shared with the audit trail by default. Override it independently when the access log needs different rules:

'access_log' => [
    'excluded_attributes' => ['password', 'pin', 'otp'],
],

Setting it to null falls back to lara-util-x.audit.excluded_attributes.

Changed in v1.5.5. current_password, new_password, api_key, and client_secret were added to the defaults, redaction now reaches nested keys instead of only top-level ones, and query-string parameter names are matched case-insensitively.

Changed in v1.5.4. Earlier releases stored json_encode($request->all()) verbatim, so placing the middleware on a login route wrote plaintext passwords into access_logs, and fullUrl() persisted any token passed as a query parameter. If you have been running this middleware on authentication routes, treat the existing rows as compromised credentials and purge them.

Pruning

Access logs accumulate on every request. AccessLog is prunable, but php artisan model:prune on its own will never find it: without --model the command only scans app/Models, so a model living in vendor/ is never discovered.

Name the model explicitly:

use Illuminate\Support\Facades\Schedule;

Schedule::command('model:prune', [
    '--model' => [\LaraUtilX\Models\AccessLog::class],
])->daily();

--model limits that run to the models you list. Keep this schedule separate from any model:prune you already run for your own models, rather than merging them into one command, or one of the two sets will stop being pruned.

Retention is configurable, and null keeps rows indefinitely:

'access_log' => [
    'retention_days' => 30,
],

Added in v1.5.4. The table previously grew without bound.

Publish Migrations

You can publish the package's migrations if you want to customise the table:

Models

The AccessLog model is not publishable. A published copy kept its LaraUtilX namespace while landing in app/Models, so Composer never autoloaded it and every call still resolved to the package's class. Extend it instead:

namespace App\Models;

class AccessLog extends \LaraUtilX\Models\AccessLog
{
    //
}

Migrations

You can publish the package's migrations through this command:

php artisan vendor:publish --tag=lara-util-x-migrations