
An order suddenly moved to refunded.
You open the audit log and see:
Order #42 updated
By: System
Changed: status = refunded
Great.
But who actually clicked the button?
Support? The customer? An admin? A scheduled task? Another job?
The audit trail has no answer.
That problem is what led me to build Yammi Audit Log.
The problem with audit logs in queue-heavy applications
Most Laravel audit packages are good at answering one question:
What changed?
In modern applications, that's often not enough.
A typical flow looks like this:
HTTP request
↓
Service
↓
Queue
↓
Job
↓
Model update
By the time the database row is updated, the actual write is being performed by a queue worker.
The audit log ends up saying something like:
Changed by: System
or
Changed by: Queue Worker
Technically correct.
Practically useless.
During an incident, the question people actually ask is:
Who initiated this change, and what execution path led to it?
Tracking provenance instead of just changes
Yammi Audit Log records three separate concepts for every change.
Actor
Who directly executed the write.
For example:
{
"type": "job",
"label": "ChargeOrder"
}
Actors can be:
- user
- job
- command
- scheduler
- system
Origin
Who started the entire chain.
Imagine a user clicks "Pay".
That dispatches a job.
The job dispatches another job.
The final database write is executed by a queue worker, but the origin remains:
{
"type": "user",
"label": "John Doe"
}
Correlation ID
A single identifier shared across the entire execution chain.
Request
↓
Job A
↓
Job B
↓
Order #42 updated
That correlation ID allows you to reconstruct the full cascade.
[trace screenshot]
Now the audit log answers:
What caused this change?
instead of only:
What code wrote this row?
How it works
The implementation is intentionally simple.
The package listens globally to Eloquent model events:
created
updated
deleted
restored
When a change occurs:
- A field-level diff is built.
- Sensitive values are redacted (
password,token,api_key, etc.). - The current actor is resolved.
- The origin context is serialized into queued jobs.
- A correlation ID ties the entire chain together.
- An audit record is written.
No per-model registration is required for basic auditing.
User::first()->update([
'name' => 'John Doe',
]);
The change is captured automatically.
[dashboard screenshot]
A few things built on top of that context
Once every change carries execution provenance, some interesting features become possible.
Tamper-evident history
Optionally hash-chain every record and verify integrity later.
php artisan audit-log:verify
Time Machine
Reconstruct the exact state of a record at any point in time.
AuditLog::stateAt(
Order::class,
42,
'2026-03-03'
);
Anomaly Detection
Detect suspicious patterns such as:
- change bursts
- mass deletions
- off-hours activity
- unusually large write cascades
Because the package already understands the execution chain, it can reason about behavior, not just rows.
Try it
Installation takes a couple of minutes:
composer require romalytar/yammi-audit-log-laravel
php artisan migrate
php artisan audit-log:ui enable
Then open:
/audit-log
and start seeing not only what changed, but how that change got there.
GitHub: https://github.com/RomaLytar/yammi-audit-log
If this kind of execution traceability is something you've missed in Laravel, I'd love to hear your feedback.

Top comments (0)