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

noin/filament-activity-log

Filament Activity Log brings a clean, searchable activity timeline into your Filament admin panel, so you can browse, filter and inspect model changes recorded by spatie/laravel-activitylog without leaving the dashboard.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install the foundation first. This package is a UI + ergonomics layer over spatie/laravel-activitylog. The activity table, Activity model, and LogsActivity trait all come from Spatie, so get that working before you touch the Filament side:

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

2. Add the Filament package.

composer require noin/filament-activity-log

3. Register the plugin on the panel that should show the log page (not necessarily every panel — see Gotchas on guards):

use Noin\FilamentActivityLog\ActivityLogPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->id('admin')
        ->plugin(ActivityLogPlugin::make());
}

Namespaces/class names above reflect the package's public surface; because this is a small (2-star, single-maintainer) package with a recent release date, confirm exact names against the README and src/ before wiring into a production panel. Budget 15 minutes reading src/ — it's tiny.

Where to look first, in order:

  1. README.md — plugin registration + whether a Resource or a Page is published.
  2. src/ — the plugin class and the Log viewer (Page/Resource). This tells you whether the viewer is a Filament Resource (routable, /{id} detail pages) or a single Page (list-only).
  3. config/ — for log_name, causer resolution, and which models get listed in the filter dropdown.
  4. vendor/spatie/laravel-activitylog/config/activitylog.php — the actual behavior knobs live here, not in the Filament package.

First use case — the 20-minute win: log a single Eloquent model and see it appear in the panel.

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

class Post extends Model
{
    use LogsActivity;

    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()
            ->logFillable()
            ->logOnlyDirty()
            ->dontSubmitEmptyLogs();
    }
}

Then create/update/delete a Post in the panel and open the activity page. If you see a row with old/new attribute JSON, the integration is working and everything else is polish.

Implementation Patterns

Trait on the model, not the Resource. Resist the urge to log inside Filament actions. Model-level logging captures writes from seeders, imports, queues, tinker, and other panels — the panel viewer is just a read surface over that data. Logging in EditAction closures gives you a log that lies the moment someone uses artisan tinker.

Pick your attribute strategy deliberately. Three common shapes:

// A. Everything, dirty only — good for audit/compliance, noisy for big tables
LogOptions::defaults()->logAll()->logOnlyDirty()->dontSubmitEmptyLogs();

// B. Fillable only — the pragmatic default
LogOptions::defaults()->logFillable()->logOnlyDirty()->dontSubmitEmptyLogs();

// C. Explicit allow/deny — best when one column churns (e.g. updated_at, sort_order)
LogOptions::defaults()
    ->logOnly(['title', 'status', 'published_at'])
    ->logOnlyDirty()
    ->dontSubmitEmptyLogs();

For any model with a sort_order, drag-reorder column, or last_seen_at, use C. Otherwise a single drag-and-drop reorder produces N activity rows and buries the meaningful entries.

Enrich entries with tapActivity so the viewer is actually readable to non-developers. The description column is what people scan; the JSON is what they click into.

public function tapActivity(Activity $activity, string $eventName): void
{
    $activity->properties = $activity->properties->merge([
        'tenant_id' => Filament::getTenant()?->id,
        'panel'     => Filament::getCurrentPanel()?->getId(),
    ]);
}

Storing the panel ID is the cheapest multi-panel guard: one activity_log table, filterable per panel, no schema forks.

Log non-Eloquent events explicitly. The trait only covers model lifecycle. For "exported report", "impersonated user", "changed settings" use the fluent API, and always set performedOn so the viewer's subject link works:

activity('admin')
    ->performedOn($user)
    ->causedBy(auth()->user())
    ->withProperties(['ip' => request()->ip()])
    ->event('impersonated')
    ->log('Started impersonating user');

Soft deletes and restore. LogsActivity emits deleted and restored automatically when the model uses SoftDeletes — that's the restore support the package advertises. The gotcha is force deletes and deleted_at appearing in the attribute diff; if you use logOnlyDirty, restored entries are usually clean, but confirm by toggling a record before you trust the view.

Custom descriptions beat raw attributes. Override the event label so the viewer reads like a sentence instead of a diff:

public function getDescriptionForEvent(string $eventName): string
{
    return "Post \"{$this->title}\" was {$eventName}";
}

Batch / noisy contexts. Wrap imports and mass updates with activity()->withoutLogs(fn () => ...) or Model::withoutLogging(...) when you're migrating data. Otherwise a 10k-row import creates 10k activity rows that you'll be pruning for months.

Extend the viewer rather than forking it. Because it's a Filament Resource/Page under the vendor path, the extension points are: publish the views, register your own ActivityLogResource subclass, or add a table filter via the resource's table() method (if exposed). For anything beyond cosmetics — custom filters, export, retention UI — the honest move is to copy the page into app/Filament/ and own it. A 2-star package is not a stable extension surface.

Gotchas and Tips

Causer guard mismatch is the #1 support ticket for every Filament activity-log integration. Spatie's default guard is web, but Filament admins authenticate on your panel guard. Result: every row has causer_id = null and the log says "System" for everything. Fix in config/activitylog.php:

'default_auth_guard' => null, // or explicitly 'admin'

null lets Spatie fall back to Auth::guard() defaults, which is usually right in a single-panel app. In multi-guard apps set it explicitly and remember to re-check after adding a second panel.

Migration conflicts. If activity_log already exists (Spatie already installed elsewhere, or a prior package), the Filament package's publish step may collide. Publish only the config if the migration already ran; don't blindly vendor:publish --all into an existing app.

logOnlyDirty() without dontSubmitEmptyLogs() is a footgun. A no-op save ($model->save() with no changes) produces an activity row with an empty properties diff. Combined with a Resource that saves on every modal open (form save() on unchanged data), you get phantom audit entries — which destroys trust in the log faster than having no log at all.

Schema growth. activity_log is unbounded by design. Before go-live add an index on (subject_type, subject_id) and (causer_type, causer_id) — Spatie's migration includes these, but verify — and decide a retention policy now. A prune command from day one beats a 3GB table in month eight. Also consider properties size: logAll() on a model with a large JSON column will store full snapshots twice per update.

logAll() on models with secrets. Password hashes, API tokens, remember tokens. Spatie does not redact; the viewer will render them in the properties JSON. Either exclude explicitly (logExcept(['password', 'remember_token', 'api_token'])) or mark the columns $hidden and exclude — $hidden does not influence the log.

The viewer's model filter list. Packages of this shape usually build a subject-type dropdown from models that have activity records, or from a config array. If the dropdown is empty or stale, that's a config/registration issue, not a bug in the log — check the config for the model map.

Authorization. A log viewer is a read of your entire change history — arguably more sensitive than the models themselves. Gate it (ActivityLogResource::canViewAny() or the equivalent) and don't ship it to a panel where User role can reach it by default. Also: if you logged with dontLogIfAttributesChangedOnly or excluded fields, the viewer won't show them — don't let anyone believe the log is a complete record.

Testing. There's no Activity::fake() in Spatie. Assert against the table:

$this->assertDatabaseHas('activity_log', [
    'subject_type' => Post::class,
    'subject_id'   => $post->id,
    'event'        => 'updated',
    'log_name'     => 'default',
]);

For the Filament side, use Livewire component tests against the page's table — and test the auth guard path explicitly, since a passing actingAs($user) on the default guard will mask the causer bug that breaks production.

Version pinning. With a July 2026 release and 2 stars, pin the minor version ("noin/filament-activity-log": "~1.0") and be prepared for breaking changes on Filament major bumps — the panel plugin APIs (Plugin, Panel::plugin()) have shifted across Filament 3→4. Test the upgrade in a branch, not in the lockfile on a Friday.

Escape hatch. If the packaged viewer stalls on a Filament major release, you lose nothing structurally: Spatie keeps logging, and a plain ActivityResource with a table of description, causer, created_at and an infolist for properties is ~80 lines. Treat this package as a time-saver, not a dependency you can't unwind.

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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource