owen-it/laravel-auditing
Audit Eloquent model changes in Laravel with a simple trait. Automatically record create/update/delete events, track who/when/what changed, and retrieve audit history for reports, compliance, and anomaly detection. Flexible drivers and rich metadata support.
Installation:
composer require owen-it/laravel-auditing
Publish the migration and config:
php artisan vendor:publish --provider="OwenIt\Auditing\AuditingServiceProvider" --tag="migrations"
php artisan vendor:publish --provider="OwenIt\Auditing\AuditingServiceProvider" --tag="config"
Run the migration:
php artisan migrate
Enable Auditing on a Model:
Use the Auditable trait in your Eloquent model:
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;
use OwenIt\Auditing\AuditingTrait;
class User extends Model implements AuditableContract
{
use AuditingTrait;
}
First Use Case: Trigger a model update to generate an audit log:
$user = User::find(1);
$user->name = 'Updated Name';
$user->save(); // Audit log created automatically
Retrieve Audit Logs:
$audits = $user->audits; // Collection of Audit models
$audits->each(function ($audit) {
echo $audit->created_at->diffForHumans();
echo $audit->getChanges();
});
Model-Level Auditing:
AuditingTrait for automatic auditing on created, updated, and deleted events.getAuditEvents():
public function getAuditEvents()
{
return ['created', 'updated', 'deleted', 'restored'];
}
Dynamic Attribute Handling:
getAuditableAttributes():
public function getAuditableAttributes()
{
return ['*']; // Audit all attributes
// OR
return ['name', 'email']; // Audit specific attributes
// OR
return ['*', '!password']; // Audit all except password
}
Resolver Integration:
// Config: config/auditing.php
'resolvers' => [
'user' => \OwenIt\Auditing\Resolvers\UserResolver::class,
'ip' => \OwenIt\Auditing\Resolvers\IpResolver::class,
],
Multi-User Auditing:
setAuditUser():
$user->setAuditUser($currentUser)->update(['name' => 'New Name']);
Querying Audits:
Audit model:
$audits = Audit::where('auditable_type', User::class)
->where('event', 'updated')
->with('user')
->get();
Laravel Policies: Use auditing to log policy violations:
public function delete(User $user, Post $post)
{
if (!$user->can('delete', $post)) {
$post->audits()->create([
'event' => 'access_denied',
'changes' => json_encode(['action' => 'delete']),
]);
abort(403);
}
}
API Versioning: Tag audits by API version:
$audit = $user->audits()->create([
'event' => 'updated',
'changes' => $changes,
'tags' => ['api_v1'],
]);
Custom Drivers: Extend AuditDriver for non-database storage (e.g., Elasticsearch):
class ElasticsearchDriver extends AuditDriver
{
public function log($model, $event, $changes)
{
// Custom logic to index audits in Elasticsearch
}
}
Performance Overhead:
protected $auditDisabled = true;
audit() method to manually trigger audits (avoids event overhead):
$user->audit('manual_event', ['key' => 'value']);
Attribute Serialization:
getAuditableValue() to customize:
public function getAuditableValue($key)
{
return $key === 'relationship' ? $this->relationship->id : parent::getAuditableValue($key);
}
Event Ordering:
bootAuditing() to adjust:
protected static function bootAuditing()
{
static::updated(function ($model) {
// Custom logic before auditing
});
parent::bootAuditing();
}
Soft Deletes:
deleted audits. Use restored for restore():
public function getAuditEvents()
{
return ['created', 'updated', 'deleted', 'restored'];
}
Resolver Conflicts:
OwenIt\Auditing\Contracts\Resolver. Avoid naming collisions:
// config/auditing.php
'resolvers' => [
'custom' => \App\Resolvers\CustomResolver::class,
],
Audit Logs:
config/auditing.php:
'debug' => env('AUDITING_DEBUG', false),
storage/logs/laravel.log for audit-related errors.Missing Audits:
AuditableContract and uses AuditingTrait.auditDisabled or skipAuditing() calls.Resolver Issues:
$resolver = app(\OwenIt\Auditing\Resolvers\UserResolver::class);
$resolver->resolve($model);
Database Conflicts:
audits table exists and matches the migration schema. Common issues:
updated_at column (added in v4.1.0).auditable_id casting (fixed in v8.0.3).Custom Audit Models:
OwenIt\Auditing\Models\Audit:
class CustomAudit extends Audit
{
protected $casts = [
'changes' => 'array',
'options' => 'json',
];
}
'audit_model' => \App\Models\CustomAudit::class,
Dynamic Exclusions:
getAuditableExclusions() for context-aware exclusions:
public function getAuditableExclusions()
{
return $this->isAdmin() ? ['password'] : [];
}
Event-Specific Logic:
getAuditEvent() to customize events dynamically:
public function getAuditEvent()
{
return $this->isForceUpdating() ? 'force_updated' : parent::getAuditEvent();
}
Attribute Redaction:
AttributeRedactor:
$redactor = new AttributeRedactor();
$redacted = $redactor->redact($model->toArray(), ['password']);
Batch Auditing:
\OwenIt\Auditing\Facades\Auditing::disable();
User::where('active', false)->update(['active' => true]);
\OwenIt\Auditing\Facades\Auditing::enable();
How can I help you explore Laravel packages today?