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

Activity Log Laravel Package

backpack/activity-log

Backpack Activity Log adds an audit trail to your Laravel admin panel: automatically records user actions and model changes, then lets admins browse, filter, and inspect activity from a dedicated Backpack CRUD screen. Built on spatie/laravel-activitylog for simple setup.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin Backpack CRUD wrapper around spatie/laravel-activitylog. It does not log anything by itself — it gives you an admin screen to browse, filter, and delete the activities Spatie records. So install Spatie first if you haven't already:

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

composer require backpack/activity-log

The package ships its own migration for a activity_log view helper (adds columns to the Spatie table if missing) — run php artisan migrate after install to pick it up. Then register a CRUD route in routes/backpack/custom.php:

Route::crud('activity-log', \Backpack\ActivityLog\Http\Controllers\ActivityLogCrudController::class);

and add the sidebar link in resources/views/vendor/backpack/base/inc/sidebar_content.blade.php:

<li class="nav-item"><a class="nav-link" href="{{ backpack_url('activity-log') }}"><i class="la la-history nav-icon"></i> Activity Log</a></li>

First use case: create a model that uses LogsActivity, do something to it, then reload /admin/activity-log — you'll see the entry with causer, subject, description, and timestamp. If you see nothing, the culprit is almost always that Spatie's migration wasn't run, or the model isn't using the trait.

Where to look first: config/backpack/activity-log.php (columns, filters, date format), and Spatie\Activitylog\ActivitylogServiceProvider for the underlying config (activitylog.php — subject_returns_soft_deleted_models, delete_records_older_than_days, default_log_name).

Implementation Patterns

Instrumenting models in the first place. The activity-log package does not touch model code — you add Spatie's trait yourself:

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

class Invoice extends Model
{
    use LogsActivity;

    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()
            ->logOnly(['status', 'total'])
            ->logOnlyDirty()
            ->useLogName('invoice')
            ->setDescriptionForEvent(fn (string $event) => "Invoice {$event}");
    }
}

Then useLogName('invoice') becomes a filterable value in the Backpack list view — this is where the package pays off, because you can drive day-to-day triage from the admin UI instead of tailing logs.

Audit trail per resource. A common pattern is one log name per domain (invoice, user, payment) so support staff can filter to a subsystem. The CRUD controller exposes Spatie's log_name column as a filter automatically; you only need to make sure you set useLogName() consistently in each model.

Manual logging for non-model events. Not everything is an Eloquent update. Log auth events, exports, or webhook failures with activity()->performedOn($model)->causedBy($user)->withProperties([...])->log('...'). These appear in the same CRUD screen with no extra code.

Causer resolution. Set 'causer' => \App\Models\User::class in config/activitylog.php (Spatie side) and, importantly, ensure your Auth::user() is resolved consistently — Backpack uses the same guard as your admin auth, so causer_id will be the admin user. If you have multiple guards (customers and admins), you need ->causedBy(auth('admin')->user()) explicitly, because Spatie's default causer resolution uses the default guard.

Customizing the CRUD controller. Extend the shipped controller rather than editing it:

class ActivityLogCrudController extends \Backpack\ActivityLog\Http\Controllers\ActivityLogCrudController
{
    protected function setupListOperation(): void
    {
        parent::setupListOperation();
        $this->addColumn(['name' => 'properties', 'type' => 'json', 'label' => 'Payload']);
    }
}

Override setupListOperation, setupFilterOperation, and setupShowOperation — never the constructor. Columns are configured with Backpack's column API (text, select_multiple, datetime, json).

Compliance/retention. If you need logs kept for a fixed period, use Spatie's activitylog:clean command (scheduled) rather than building deletion into the CRUD. Keep the UI read-mostly.

Wiring into detail pages. A nice integration: add an activity_log panel to your InvoiceCrudController::setupShowOperation using Backpack's view widget showing recent activities for $entry->id. Query with Spatie\Activitylog\Models\Activity::forSubject($entry)->latest()->take(20)->get().

Gotchas and Tips

The migration you'll forget. The package's migration adds columns to the Spatie activity_log table. If you install it before Spatie's migration, ordering can put the alter before the create. Install Spatie, migrate, then install backpack/activity-log, then migrate. On a fresh CI database this bites every time.

logOnlyDirty() and empty logs. If a save doesn't change any logged attribute, Spatie writes nothing — which is correct but surprises people who expect a row on every save(). Don't assume the absence of a log means nothing happened; combine with ->logOnly([...]) and check your LogOptions.

Subject deleted or soft-deleted. Spatie stores subject_type and subject_id, not a snapshot. If the subject is hard-deleted, the Backpack show page shows a null subject and may render a broken column. For soft deletes, set 'subject_returns_soft_deleted_models' => true in config/activitylog.php. For hard deletes, consider denormalising the subject label into properties at log time.

Morph map is not applied retroactively. If you introduce Relation::morphMap(), new entries store the alias but old entries still store the FQCN. Filtering by subject_type in the CRUD will show both forms. Either migrate historical rows or accept the split.

Performance on large tables. The list view does a normal Eloquent query over activity_log. On tables with millions of rows, the unfiltered list is slow. Mitigations: index (subject_type, subject_id), (causer_type, causer_id), and created_at; set Backpack's $this->crud->setDefaultPageLength(25); and consider adding a default date-range filter in setupFilterOperation so the initial query is bounded.

Filter type quirks. properties is stored as JSON. Filtering on arbitrary property keys is not supported out of the box — you'd need a custom filter (Backpack's select2 or a date_range filter, plus a whereJsonContains). Don't expect the stock filters to search inside properties.

Causer display with multiple guards. As noted above, Auth::user() may be the wrong user if you support multiple guards. Set the causer explicitly in your logging code.

Config caching. Backpack and Spatie both publish config. php artisan config:cache in production picks up your changes, but if you override the CRUD controller be aware that the package's service provider registers routes at boot — clear route cache (route:clear) after altering the sidebar or CRUD route.

Extension points. The two useful hooks are: subclassing ActivityLogCrudController (columns, filters, show widgets), and a global Activity model override in config/activitylog.php ('activity_model' => \App\Models\Activity::class) if you need to add scopes — e.g. scopeForSubjectType, or a boot that redacts sensitive properties before saving. Don't fork the package for this; the config override is enough.

When not to reach for it. If you need per-record audit trails embedded in a resource page rather than a global log screen, the CRUD wrapper adds overhead. In that case use Spatie directly and render a view widget — the package's value is the global, filterable admin screen, not the underlying logging.

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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route