aifst/laravel-logger

Logger package for Laravel 5.6 and up

Maintainers

Package info

github.com/aifst/laravel-logger

pkg:composer/aifst/laravel-logger

Transparency log

Statistics

Installs: 100

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.11 2026-07-27 17:39 UTC

This package is auto-updated.

Last update: 2026-07-27 17:41:40 UTC


README

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)
  • actioncreated / 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
  • comment (optional) — a free-text reason for the action, attached by the application through the LogSaving event

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).

Reason for the action (comment) via the LogSaving event

Why something was deleted or changed cannot be derived from a diff — only the calling code knows it. Every entry is therefore dispatched as Aifst\Logger\Events\LogSaving after it is built and before it is saved, so a listener can enrich it in place:

use Aifst\Logger\Events\LogSaving;

Event::listen(function (LogSaving $event) {
    // $event->log — the unsaved entry, $event->model — the subject, $event->action — created/updated/deleted
    $event->log->comment = DeletionReason::current(); // e.g. a request-scoped reason
});

A common pattern is a request-scoped holder: the service that deletes a record sets the reason it received from the user, the listener copies it onto the entry, and the holder is cleared afterwards. Anything the listener writes to $event->log is persisted with the entry.

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.

Upgrading an existing install (comment)

If your logs table predates the comment column, add it with a migration:

$table->string('comment', 255)->nullable();

Existing rows keep a null comment; the LogSaving event fires regardless of the column, so add the column before any listener starts writing to it.

License

MIT.