DEV Community

Roman_14K
Roman_14K

Posted on

User X changed the order -except a queued job did. Here's the missing piece in Laravel audit logs.


An order suddenly moved to refunded.

You open the audit log and see:

Order #42 updated
By: System
Changed: status = refunded
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

or

Changed by: Queue Worker
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

Correlation ID

A single identifier shared across the entire execution chain.

Request
  ↓
Job A
  ↓
Job B
  ↓
Order #42 updated
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

When a change occurs:

  1. A field-level diff is built.
  2. Sensitive values are redacted (password, token, api_key, etc.).
  3. The current actor is resolved.
  4. The origin context is serialized into queued jobs.
  5. A correlation ID ties the entire chain together.
  6. An audit record is written.

No per-model registration is required for basic auditing.

User::first()->update([
    'name' => 'John Doe',
]);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Time Machine

Reconstruct the exact state of a record at any point in time.

AuditLog::stateAt(
    Order::class,
    42,
    '2026-03-03'
);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then open:

/audit-log
Enter fullscreen mode Exit fullscreen mode

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)