Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Activity Log Laravel Package

waseet/filament-activity-log

A Filament plugin that adds an activity log to your admin panel, automatically tracking model changes and user actions with a searchable timeline, diff views, and per-record history so you can audit what changed, when, and by whom. Built on spatie/laravel-activitylog.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites. This is a thin Filament layer over spatie/laravel-activitylog, so install the underlying package first and get it working before adding the UI. You want the activity_log table and the LogsActivity trait functioning, otherwise the Filament page will render an empty table with no obvious error.

composer require spatie/laravel-activitylog
php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-migrations"
php artisan migrate

composer require waseet/filament-activity-log

Where to look first. Open the package's src/ directory and its service provider — this package is small (72 stars, single-purpose), so the plugin class, page/resource, and any config file are the whole surface area. Check the README for the exact plugin class name and whether it targets Filament v3 or v4; the composer.json constraints tell you immediately whether it will even boot on your panel.

Register the plugin in your panel provider (app/Providers/Filament/AdminPanelProvider.php):

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugin(\Waseet\FilamentActivityLog\ActivityLogPlugin::make());
}

Confirm the namespace/class from src/ — the vendor name (waseet) and repo owner (noxoua) differ, so don't guess.

First use case. Pick one high-value model — Order, Invoice, User — add Spatie's trait, and browse its history in the panel:

use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\Activitylog\LogOptions;

class Order extends Model
{
    use LogsActivity;

    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()
            ->logOnly(['status', 'total', 'assigned_to'])
            ->logOnlyDirty()
            ->dontSubmitEmptyLogs()
            ->useLogName('order');
    }
}

Change a field in the admin UI, then open the activity page. That end-to-end loop (trait → write → Filament table) is the fastest way to validate the install.

Implementation Patterns

Segment with log names. useLogName('order') is the single most useful convention. It gives you a stable filter value in the Filament table and stops your audit view from becoming an undifferentiated firehose of every model in the app. Adopt a naming scheme early (order, user, billing, auth) and keep it consistent.

Manual business events. The trait only covers CRUD. Log domain events explicitly — this is usually where the real audit value lives:

activity('order')
    ->performedOn($order)
    ->causedBy(auth()->user())
    ->withProperties(['reason' => $reason, 'previous_status' => $old])
    ->event('refunded')
    ->log('Order refunded');

Per-record timeline. The most-requested feature from stakeholders is "show me the history of this record" rather than a global log. Embed an activity table on the resource's view/edit page — either through a relation manager on the subject model or by adding a table to a custom page that scopes to subject_type + subject_id. This is where users actually consume the log; the global page is mostly for auditors and support.

Causer discipline. Spatie resolves the causer from auth()->user() by default. That breaks in queued jobs, console commands, seeders, and impersonation. Set it explicitly:

activity()->causedBy($user)->performedOn($order)->log('...');

For impersonation, consider logging both the impersonator and the impersonated user in properties — otherwise support staff actions look like customer actions in the audit trail.

Authorization. A global activity log is sensitive. Gate the page: implement canViewAny() or wire it to a permission (Spatie Permission, Filament Shield). By default Filament pages are visible to any panel user who can reach the panel — do not ship the log page to a customer-facing panel without a policy.

Multi-tenancy. If you use tenancy, add a tenant_id to activity_log (custom migration) and populate it via a tapActivity() hook on your models, then scope the Filament query. The package won't do this for you, and retrofitting it after the table has millions of rows is painful.

Enrichment with tapActivity. Add IP, user agent, request ID, or source channel to every activity row:

protected function tapActivity(Activity $activity, string $eventName): void
{
    $activity->properties = $activity->properties->merge([
        'ip' => request()->ip(),
        'source' => app()->runningInConsole() ? 'console' : 'web',
    ]);
}

Retention. Schedule pruning from day one:

// routes/console.php or Kernel
Schedule::command('activitylog:clean')->daily();

Configure config/activitylog.php (delete_records_older_than_days) and, for regulated environments, don't prune — archive to cold storage instead.

Testing. There's no fake for this; assert against the table:

$this->assertDatabaseHas('activity_log', [
    'log_name' => 'order',
    'event' => 'updated',
    'subject_id' => $order->id,
]);

Wrap noisy setup in Activity::disableLogging() / Activity::enableLogging() if your seeders or factories bloat the table during tests.

Gotchas and Tips

  • Empty page means empty table, not a broken plugin. Ninety percent of "it doesn't work" reports are a missing migration, a model without the trait, or logging disabled globally. Check activity_log row count first.
  • logOnlyDirty + dontSubmitEmptyLogs are non-negotiable for day-to-day sanity. Without them, every save() produces a row, including no-op saves from Filament forms that re-submit unchanged fields.
  • Never log passwords, tokens, or raw request payloads. Use logOnly() as an allowlist rather than logAll() as a denylist — a denylist fails silently the moment someone adds a sensitive column. Also check $hidden interactions if you log full attribute arrays.
  • Orphaned subjects. Deleting a model leaves activity rows pointing at a missing subject; Filament tables that eager-load the subject relationship will show blanks or, worse, throw on the morph. Decide up front: cascade-delete the logs, soft-delete the subject, or accept null and render a fallback.
  • Performance creep. activity_log is append-only and grows fast. Ensure indexes on subject_type/subject_id, causer_type/causer_id, log_name, and created_at — Spatie's default migration covers some but not necessarily the composite you filter on in Filament. Paginate and add date-range filters rather than letting users load "all".
  • Causer is null in queues. This is the most common audit defect. If a job modifies data, the log row will have no causer unless you pass one through the job payload.
  • Event naming collisions. Spatie's default created/updated/deleted events blur together with custom events (approved, refunded). Standardize on a small vocabulary and document it — filters and reports rely on exact strings.
  • Timezone in date filters. Filament filters run on created_at in the app timezone; if your DB stores UTC and users think in local time, audit queries will be off by hours in exactly the situations where they matter (incident review).
  • Version pinning. A 72-star single-maintainer package is a dependency you should pin and review at upgrade time. Filament's major-version churn (v3 → v4) routinely breaks plugins; check the package's composer.json constraints and last release date (2025-05-11) against your Filament version before committing. Budget for the possibility of forking — the surface area is small enough that a fork is cheap.
  • Customization path. When the built-in table doesn't show the column you need (causer name, subject label, properties JSON), prefer extending/publishing the resource or view over patching vendor/. If the package doesn't expose a publishable view, a small custom Filament page querying Spatie\Activitylog\Models\Activity directly is often faster than fighting the abstraction.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk