Laravel activity log is a package or custom implementation that records user actions and system events in your application, storing them in a database table for auditing, debugging, and user history. The most popular solution is the spatie/laravel-activitylog package, which provides a simple, fluent API to log events with context like causer, subject, and properties.
Why You Need an Activity Log
An activity log answers the question “who did what, when, and why.” Without it, you rely on server logs that are hard to read, or on database timestamps that only show the last update. A proper activity log gives you a trail of every meaningful action: user logins, profile updates, order status changes, admin deletions, and even automated jobs. This is essential for compliance (e.g., GDPR or HIPAA), debugging user-reported issues, and building audit trails for financial or medical applications.
Consider a typical e-commerce app: a customer claims they never received a refund. With an activity log, you can see exactly when the refund was initiated, by which admin, and what the system response was. Without it, you’re guessing.
How the Spatie Package Works
The spatie/laravel-activitylog package is the de facto standard. It adds an activity_log table with columns for log_name, description, subject_type and subject_id (polymorphic), causer_type and causer_id, properties (JSON), and timestamps. You log an event like this:
activity()->log('User updated their profile');
activity()
->performedOn($order)
->causedBy($user)
->withProperties(['status' => 'shipped'])
->log('Order status changed');
The package also supports event-driven logging: you can attach a LogsActivity trait to an Eloquent model, and it automatically logs created, updated, and deleted events. You can customize which attributes are logged via $logAttributes and even log only changed attributes with $logOnlyDirty.
Custom Activity Log vs. Package
You might be tempted to build your own activity log with a simple migration and a model. That works for tiny projects, but the package saves you time and avoids common pitfalls:
- Handles polymorphic relations (subject and causer) out of the box.
- Provides a clean API for filtering and retrieving logs.
- Includes a
spatie/laravel-activitylogevent system that integrates with Laravel’s model events. - Offers a
tapmethod to modify the activity before saving. - Supports batch logging with
logBatch().
If your needs are very simple (just log user logins), a custom table with user_id, action, and timestamp is fine. But as soon as you need to associate logs with multiple models or filter by causer, the package is worth it.
Common Problems and Solutions
One frequent issue is that the activity log grows huge, slowing down queries. Solutions include pruning old logs (a scheduled job to delete records older than X months) and indexing the subject_type, subject_id, and causer_id columns. Another problem is logging too much—every model update creates a row, which is noisy. Use $logOnlyDirty and $logAttributesToIgnore to filter out irrelevant changes.
Another gotcha: when you log inside a model event like updated, the causer is often null if the action is performed by a queued job or CLI command. Make sure to explicitly set the causer with causedBy($user) when needed. Also, be aware that properties are stored as JSON, so you can’t easily query them in older MySQL versions—use whereJsonContains in Laravel 5.6+ or store key-value pairs in a separate table if you need complex queries.
How to Choose the Right Logging Approach
Ask yourself these questions before deciding:
- Do you need to audit who changed what on a specific record? → Use the package with model events.
- Do you need a simple trail of user actions like logins and exports? → A custom table with user_id, action, and timestamp is enough.
- Do you need to store request data (IP, user agent)? → Add those columns to your activity table or use a middleware.
- Do you need to log system events (cron jobs, webhooks)? → The package works fine, just omit the causer.
For most Laravel apps, the Spatie package is the recommended choice because it’s well-tested, maintained, and flexible. For a lightweight alternative, consider jeremykenedy/laravel-logger or owen-it/laravel-auditing—each has a different focus (the latter is more about auditing model changes).
Performance and Storage Considerations
Activity logs are write-heavy. Every action inserts a row, so you need to think about storage and query speed. Use a separate database connection or even a different storage engine (like MyISAM or a NoSQL store) if you have very high traffic. In practice, for most apps, a standard MySQL table with proper indexes is fine up to a few million rows. Enable query caching or use Laravel’s cache() to store recent activity for quick display. Also, consider using a queue to write logs asynchronously—the package supports this via the queue config option.
Activity Log Template
Activity Log Table Template
| ID | Log Name | Description | Subject Type | Subject ID | Causer Type | Causer ID | Properties (JSON) | Created At |
|---|---|---|---|---|---|---|---|---|
| 1 | default | User updated profile | App\Models\User | 42 | App\Models\User | 42 | {“name”:”John”} | 2025-01-01 10:00:00 |
| 2 | default | Order status changed | App\Models\Order | 1001 | App\Models\User | 7 | {“status”:”shipped”} | 2025-01-02 14:30:00 |
Sample data—fictional.
You can use this table structure as a starting point for a custom activity log. Adjust column names to fit your needs.
Frequently Asked Questions
How do I install the Spatie activity log package?
Run composer require spatie/laravel-activitylog, then publish the migration with php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="migrations" and run php artisan migrate. The package is then ready to use.
Can I log activities without a logged-in user?
Yes. The causer is optional. If you don’t call causedBy(), the causer will be null. This is useful for system events like cron jobs or API calls from external services.
How do I retrieve all activities for a specific model?
Use the activities() relationship on the subject model if you’ve added the LogsActivity trait, or query the Activity model directly: Activity::where('subject_type', get_class($model))->where('subject_id', $model->id)->get().
Does the activity log slow down my app?
It can, if you log too much and don’t index properly. Use queues, prune old logs, and avoid logging trivial changes. The package is efficient, but you should monitor your database size.
Can I store additional data like IP address or user agent?
Yes. You can add custom properties via withProperties(['ip' => request()->ip()]) or use middleware to automatically add them. The package stores properties as JSON, so you can include any serializable data.