oluokunkabiru / laravel-audit-log-trail
A robust, diff-aware audit trail package for Laravel Eloquent models.
Package info
github.com/oluokunkabiru/laravel-audit-log-trail
pkg:composer/oluokunkabiru/laravel-audit-log-trail
Requires
- php: ^8.2
- illuminate/database: ^10.0|^11.0
- illuminate/events: ^10.0|^11.0
- illuminate/queue: ^10.0|^11.0
- illuminate/support: ^10.0|^11.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^8.0|^9.0
- pestphp/pest: ^2.0
- pestphp/pest-plugin-laravel: ^2.0
This package is auto-updated.
Last update: 2026-04-03 11:02:53 UTC
README
A fluent, diff-aware audit trail for Eloquent models. Logs exactly what changed, who changed it, and when — with multi-tenant support, a swappable driver system, and a clean suppression API.
Installation
composer require oluokunkabiru/laravel-audit-log-trail
Publish and run the migration:
php artisan vendor:publish --tag=audit-migrations php artisan migrate
Optionally publish the config:
php artisan vendor:publish --tag=audit-config
Quick start
Add the HasAuditTrail trait to any Eloquent model:
use Oluokunkabiru\AuditTrail\Traits\HasAuditTrail; class User extends Model { use HasAuditTrail; }
That's it. Every created, updated, deleted, and restored event is now logged automatically.
Controlling which fields are logged
class User extends Model { use HasAuditTrail; // Only log these fields protected array $auditInclude = ['name', 'email', 'role']; // Or: exclude specific fields (stacks with global_exclude in config) protected array $auditExclude = ['api_token', 'two_factor_secret']; }
Querying audit logs
use Oluokunkabiru\AuditTrail\Models\AuditLog; // All changes to a specific model instance AuditLog::forModel($user)->latest()->get(); // Filter by event type AuditLog::forModel($user)->event('updated')->get(); // Who changed the email field? AuditLog::forModel($user)->forField('email')->get(); // All deletes in the last 7 days AuditLog::event('deleted')->since(now()->subDays(7))->get(); // By actor AuditLog::byActor($admin)->today()->get();
Via the model relationship:
$user->auditLogs()->latest()->limit(10)->get(); $user->latestAudit;
Suppression
use Oluokunkabiru\AuditTrail\Facades\Auditor; // Suppress all logging inside the callback Auditor::suppress(function () { User::factory()->count(1000)->create(); }); // Suppress on a specific model instance $user->withoutAudit(fn() => $user->update(['name' => 'Silent Bob']));
Custom events
Auditor::log($user, 'password_reset', before: ['method' => 'email']); Auditor::log($order, 'status_changed', before: ['status' => 'pending'], after: ['status' => 'paid']);
Multi-tenancy
In config/audit.php:
'tenant_resolver' => fn() => auth()->user()?->organization_id,
Query scoped to a tenant:
AuditLog::forTenant($tenantId)->latest()->get();
Async writes (recommended for production)
AUDIT_QUEUE_ENABLED=true AUDIT_QUEUE_CONNECTION=redis AUDIT_QUEUE_NAME=audits
Pruning old logs
# Delete logs older than 365 days (or your configured retention) php artisan audit:prune # Override days via option php artisan audit:prune --days=90
Schedule it in routes/console.php:
Schedule::command('audit:prune')->daily();
Configuration reference
// config/audit.php return [ 'driver' => 'database', 'table' => 'audit_logs', 'queue' => ['enabled' => false, 'connection' => null, 'queue_name' => 'audits'], 'tenant_resolver' => null, 'actor_resolver' => null, 'prune' => ['keep_days' => 365], 'global_exclude' => ['password', 'remember_token'], 'metadata' => ['capture_url' => true, 'capture_ip' => true, 'capture_user_agent' => false], ];
Custom drivers
Implement the AuditDriver contract and bind it in a service provider:
use Oluokunkabiru\AuditTrail\Drivers\Contracts\AuditDriver; class ElasticsearchAuditDriver implements AuditDriver { public function log(AuditEntry $entry): void { /* ... */ } public function prune(int $keepDays): int { /* ... */ } } // In AppServiceProvider::register(): $this->app->bind(AuditDriver::class, ElasticsearchAuditDriver::class);
Listening to audit events
use Oluokunkabiru\AuditTrail\Events\AuditLogged; Event::listen(AuditLogged::class, function (AuditLogged $event) { // $event->entry is an AuditEntry value object logger()->info('Audit recorded', $event->entry->toArray()); });
Running tests
composer test
License
MIT