spatie/laravel-activitylog
Log user and model activity in Laravel with a simple API. Automatically record Eloquent events, track subjects and causers, attach custom properties, and query everything via the Activity model. Stores logs in the activity_log table.
Installation:
composer require spatie/laravel-activitylog
php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-migrations"
php artisan migrate
(Optional: Publish config with --tag="activitylog-config")
First Log Entry:
use Spatie\Activitylog\Facades\Activity;
Activity::log('User accessed dashboard');
Query Logs:
$logs = \Spatie\Activitylog\Models\Activity::latest()->take(10)->get();
// Log user actions on a model
$post = Post::find(1);
activity()->performedOn($post)->causedBy(auth()->user())->log('Published post');
// Retrieve recent activity for a user
$recentActivity = \Spatie\Activitylog\Models\Activity::where('causer_id', auth()->id())
->latest()->take(5)->get();
// Basic logging
activity()->log('System backup initiated');
// With metadata
activity()
->performedOn($invoice)
->causedBy($user)
->withProperties(['status' => 'paid'])
->log('Invoice marked as paid');
// Using custom log name
activity('audit')->log('Admin deleted user');
// In Post model
use Spatie\Activitylog\Traits\LogsActivity;
class Post extends Model
{
use LogsActivity;
protected static $logAttributes = ['title', 'content'];
protected static $logOnlyDirty = true;
}
Triggered automatically on:
created (description: "created")updated (description: "updated")deleted (description: "deleted")// Find all activities for a model
$postActivities = \Spatie\Activitylog\Models\Activity::where('subject_type', Post::class)
->where('subject_id', $post->id)->get();
// Filter by description
$paymentActivities = \Spatie\Activitylog\Models\Activity::where('description', 'like', '%paid%')->get();
// Get attribute changes
$activity = \Spatie\Activitylog\Models\Activity::find(1);
$changes = $activity->attribute_changes;
// Controller method
public function auditLog()
{
$logs = \Spatie\Activitylog\Models\Activity::with(['causer', 'subject'])
->latest()->paginate(20);
return view('admin.audit', compact('logs'));
}
// Override default descriptions
use Spatie\Activitylog\LogOptions;
class Post extends Model
{
use LogsActivity;
protected static function getActivityDescriptionForEvent($eventName)
{
return match($eventName) {
'created' => 'New post published',
'updated' => 'Post content updated',
default => parent::getActivityDescriptionForEvent($eventName),
};
}
}
// Only log if sensitive data changes
$post->update([
'title' => 'New Title',
'content' => 'Updated Content',
'is_published' => true,
]);
if ($post->wasChanged('is_published')) {
activity()->performedOn($post)->log('Post publication status changed');
}
// Get all activities for current user
$myActivities = auth()->user()->activities()->latest()->get();
// Get activities with eager loading
$activities = \Spatie\Activitylog\Models\Activity::with(['causer', 'subject'])
->whereHas('subject', fn($q) => $q->where('type', Post::class))
->get();
// app/Models/CustomActivity.php
use Spatie\Activitylog\Models\Activity as BaseActivity;
class CustomActivity extends BaseActivity
{
protected $table = 'custom_activity_logs';
protected $connection = 'mysql_custom';
}
// Update config
'activity_model' => \App\Models\CustomActivity::class,
Performance with Large Logs
activity_log table:
Schema::table('activity_log', function (Blueprint $table) {
$table->index(['subject_type', 'subject_id']);
$table->index(['causer_id']);
$table->index(['log_name']);
$table->index(['created_at']);
});
clean command regularly:
php artisan activitylog:clean
Circular References in Properties
activity()->withProperties([
'user_data' => auth()->user()->toArray()
])->log('Action performed');
Soft Deletes
'include_soft_deleted_subjects' => true,
UUID Models
Schema::create('activity_log', function (Blueprint $table) {
$table->id();
$table->string('subject_type');
$table->uuid('subject_id')->nullable();
// ...
});
Memory Leaks
\Spatie\Activitylog\Models\Activity::cursor()->get();
Check Logged Properties
$activity = \Spatie\Activitylog\Models\Activity::find(1);
dd($activity->properties); // View raw properties
Verify Event Logging
// Temporarily enable all logging
\Spatie\Activitylog\Activity::enableLogging();
// Check if events are firing
event(new \Illuminate\Database\Eloquent\Model\Creating($model));
Inspect Activity Model
// Dump activity structure
\Spatie\Activitylog\Models\Activity::first()->toArray();
Default Auth Driver
'default_auth_driver' => 'api',
Disabled Logging
activity()->disableLogging();
// ... critical operation ...
activity()->enableLogging();
Log Name Scoping
activity('security')->log('Failed login attempt');
activity('audit')->log('User accessed settings');
Custom Log Actions
// app/Actions/CustomLogAction.php
use Spatie\Activitylog\Actions\LogActivityAction;
class CustomLogAction extends LogActivityAction
{
protected function getDefaultDescription(): string
{
return 'Custom default description';
}
}
// Update config
'actions' => [
'log_activity' => \App\Actions\CustomLogAction::class,
],
Activity Model Observers
// app/Observers/ActivityObserver.php
use Spatie\Activitylog\Models\Activity;
class ActivityObserver
{
public function saving(Activity $activity)
{
if ($activity->description === 'created') {
$activity->description = 'New record created';
}
}
}
// Register in AppServiceProvider
Activity::observe(ActivityObserver::class);
Custom Activity Attributes
// Add custom columns to activity_log table
Schema::table('activity_log', function (Blueprint $table) {
$table->string('ip_address')->nullable();
$table->string('user_agent')->nullable();
});
// Update Activity model
class Activity extends BaseActivity
{
protected $fillable = ['ip_address', 'user_agent'];
}
// Log additional data
activity()->withProperties([
'ip' => request()->ip(),
'user_agent' => request()->userAgent(),
])->log('Action performed');
// For bulk operations
\Spatie\Activitylog\Facades\Activity::disableLogging();
// ... bulk operations ...
\Spatie\Activitylog\Facades\Activity::enableLogging();
How can I help you explore Laravel packages today?