laravel-logger maintained by aifst
Laravel Logger
A lightweight audit log for Eloquent models. Add one trait and every create / update / delete is recorded — with the acting user, a before/after diff, and an optional owner (the scope the event happened in, e.g. a project or a site).
Each entry stores:
- subject — the model the action was performed on (
model_type/model_id) - action —
created/updated/deleted - user — who caused it (
user_id) - before / after — the changed attributes (a diff for updates, the full record for create/delete)
- owner (optional) — the polymorphic scope container the event happened in (
owner_type/owner_id), or null for a global entry
Installation
Install via Composer:
composer require "aifst/laravel-logger:^1.0.0"
The service provider is auto-discovered. To register it manually, add it to config/app.php:
'providers' => [
// ...
Aifst\Logger\LoggerServiceProvider::class,
];
Publish the migration and config/logger.php, then migrate:
php artisan vendor:publish --provider="Aifst\Logger\LoggerServiceProvider"
php artisan migrate
Usage
Add the Logger trait to any model you want audited:
use Aifst\Logger\Traits\Logger;
class Course extends Model
{
use Logger;
}
By default all three events are logged. Opt out per model by overriding the flags:
protected static function loggedCreated(): bool { return true; }
protected static function loggedUpdating(): bool { return true; }
protected static function loggedDeleting(): bool { return false; }
Recording the acting user
The trait does not know your auth layer, so tell it how to resolve the current user id
(return null for system/unauthenticated actions):
protected static function loggerUserId()
{
return auth()->id();
}
Limiting the logged attributes
By default the whole record (minus id / timestamps) is captured. Restrict it to specific columns:
protected static function loggedFields(): ?array
{
return ['title', 'status'];
}
Owner scope (optional)
A log entry records the subject it happened to. You may also record an owner — the scope container the event happened in (a project, a site, a tenant…). The owner is polymorphic and nullable, so entries can be scoped to a project, to a site, or left global, without tying the log to any single entity — and it is distinct from the subject.
Override loggerOwner() to return the owning model:
protected function loggerOwner()
{
return $this->project; // or Site::current(), or null for a global entry
}
The entry then stores owner_type / owner_id (honouring a registered morph map).
Querying
The Log model exposes read helpers:
use Aifst\Logger\Models\Log;
// A model's own history (via the Logger trait's relation)
$course->logs()->latest()->get();
// By owner scope (all activity in a project)
Log::forOwner($project)->latest()->get();
Log::forOwner($project->getMorphClass(), $project->id)->get();
// By action
Log::wasCreated()->get();
Log::wasUpdated()->get();
Log::wasDeleted()->get();
// By subject entity
Log::entity($course->getMorphClass(), $course->id)->get();
// In a time window
Log::between($from, $to)->get();
// Reconstruct a model's state at a point in time
$snapshot = $course->logs()->stateOn($datetime);
before and after are returned as arrays (JSON is decoded automatically).
Upgrading an existing install (owner scope)
If your logs table predates the owner columns, add them with a migration:
$table->string('owner_type')->nullable();
$table->unsignedBigInteger('owner_id')->nullable();
$table->index(['owner_type', 'owner_id']);
Existing rows keep a null owner; nothing else changes.
License
MIT.