romalytar/yammi-audit-log-laravel
Audit log for Laravel that tracks full provenance of every change: actor, origin, and correlation ID across queues and services. Built for distributed, queue-heavy apps to trace who triggered a write and through what execution chain.
Installation:
composer require romalytar/yammi-audit-log-laravel
php artisan migrate
This creates the audit_log table and optional tables for advanced features.
First Use Case:
// No model setup required. Changes are automatically audited:
User::first()->update(['name' => 'Test']);
The change is recorded with actor (who executed it), origin (who initiated it), and correlation ID (ties the chain together).
Enable Dashboard (Optional):
php artisan audit-log:ui enable
Access the dashboard at /audit-log to view changes.
created, updated, deleted, restored) are automatically audited.// For raw SQL or Query Builder updates:
AuditLog::record($model, 'updated', ['field' => 'new_value']);
ChargeOrderJob) and origin (e.g., John Doe) for every change, even across queues.
// Example: A user triggers a job that updates a model:
User::find(1)->dispatch(new ProcessPaymentJob());
// Audit log will show:
// - Actor: ProcessPaymentJob
// - Origin: John Doe (user who triggered the job)
Time Machine:
$pastState = AuditLog::timeMachine()->getState(User::class, 1, '2023-01-01');
Reconstructs a model's state at a past timestamp.
Anomaly Detection:
AuditLog::anomaly()->detect('status_changed_to_cancelled', 5); // Alert if >5 cancellations in 1 hour
GDPR Reports:
$userData = AuditLog::gdpr()->getSubjectData(User::class, 1);
Multi-Tenancy:
AuditLog::setTenant('tenant_id_123'); // Scope logs to a tenant
$logs = AuditLog::query()
->model(User::class)
->field('status')
->value('active')
->get();
$chain = AuditLog::query()
->correlation('550e8400-e29b-41d4-a716-446655440000')
->withChain()
->get();
Async Writes:
AUDIT_LOG_WRITE_ASYNC=true
Offloads audit log writes to a queue.
Sampling High-Churn Models:
AuditLog::ignoreModel(User::class); // Skip auditing for this model
AuditLog::sampleModel(Order::class, 0.1); // Audit 10% of changes
Non-Eloquent Changes:
AuditLog::record($model, 'updated', ['field' => 'value']);
Correlation ID Leaks:
config/audit-log.php:
'redact' => ['correlation_id', 'token', 'api_key'],
Dashboard Access:
php artisan audit-log:ui enable
Retention Policy:
config/audit-log.php:
'retention' => ['days' => 365],
Multi-Tenancy Conflicts:
AuditLog::setTenant('tenant_id');
Verify Capture:
AuditLog::debug()->enable(); // Logs capture events to Laravel logs
Check Provenance Chain:
AuditLog::setOrigin() in jobs).Slow Queries:
changed_keys table for field searches:
AuditLog::query()->field('status')->value('active')->get();
Custom Actors/Origins:
config/audit-log.php:
'actors' => [
\App\Providers\CustomActorProvider::class,
],
Add Custom Fields to Logs:
AuditLog::extend(function ($log) {
$log->customField = 'value';
});
Override Default Diff:
AuditLog::diff(function ($old, $new) {
return ['custom_diff' => $old->diffAssoc($new)];
});
Integrate with SIEM:
AuditLog::stream() event listener:
AuditLog::stream(function ($log) {
// Send to Splunk/Datadog
});
Async Writes:
AUDIT_LOG_WRITE_ASYNC=true, ensure the queue worker processes audit logs promptly to avoid gaps.UI Middleware:
web middleware by default. Customize in config/audit-log.php:
'ui' => [
'middleware' => ['web', 'auth'],
],
Database Connection:
'write' => [
'connection' => 'audit_log_db',
],
Event Versioning:
'governance' => [
'event_version' => true,
],
Impersonation Handling:
AuditLog::setImpersonator('admin_id');
Bulk Operations:
AuditLog::ignore(function () {
User::where('active', false)->update(['deleted_at' => now()]);
});
Alerts for Critical Changes:
AuditLog::anomaly()->rule('critical_change', function ($log) {
return $log->field('status') === 'deleted' && $log->model === User::class;
})->threshold(1)->window('1 hour')->alertVia('slack');
GDPR Right to Erasure:
$report = AuditLog::gdpr()->generateReport(User::class, 1);
Cross-Model Traces:
$correlationId = AuditLog::getCorrelationId();
AuditLog::query()->correlation($correlationId)->get();
How can I help you explore Laravel packages today?