Step 21 of 21 · 4 minutes

Compliance by Default

The Scenario

The compliance team has a new requirement: every payment job state change must be audited — when it starts processing, when it succeeds, and especially when it fails permanently.

Adding audit logging inside every job method is error-prone and couples business logic to infrastructure concerns. There must be a better way.

The Challenge

How do we react to job state changes without modifying the jobs themselves?

The Solution

Implement a JobFilter — a hook that runs automatically on every job lifecycle event.


@Component
public class PaymentAuditFilter implements ApplyStateFilter {

    @Override
    public void onStateApplied(Job job, JobState oldState, JobState newState) {
        // runs on every state change of every job
    }
}

Because it's a Spring @Component, JobRunr picks it up automatically. No changes needed in PaymentService — the audit trail is added as a cross-cutting concern.

Other filter types let you hook in at different points:
  • JobClientFilter — when a job is created
  • ElectStateFilter — when JobRunr decides what state a job should move to
  • JobServerFilter — before and after a job is processed

Read the documentation →

Try It Yourself

Create a few payments and watch the server logs — every state transition is audited.

Force a failure by making a payment to a credit card that doesn't exist. You'll see the AUDIT | Payment job … failed permanently warning appear automatically.
You need to log in to perform write operations. You can still view the code solution and dashboard.