minvws/audit-logger
A Laravel audit logging package that records user actions and model events into a queryable audit trail. Easily log authentication attempts, CRUD changes, and custom entries with configurable storage and a clean API for reviewing activity.
composer require minvws/audit-logger
The package is minvws/nl-rdo-php-audit-logger; keep the repo name handy, since the composer name and the GitHub slug differ and the README is thin.
Read src/AuditLoggerInterface.php first — as of 1.1.0 that file exists and is the contract you bind against. Before 1.1.0 there was only a concrete class, so any older snippet you find online targeting new AuditLogger(...) still works but gives you nothing to mock. Confirm the exact method signature by reading the file rather than trusting docs; the interface landed in a minor release (PR #28) with no accompanying README rewrite, so it is the authoritative source.
The first decision is not "how do I log" but "where do the records land". The package speaks PSR-3 only, so pick the sink up front: a dedicated audit log channel, syslog, or a dedicated table/stream. In Laravel, register a singleton against the interface:
// app/Providers/AuditServiceProvider.php
public function register(): void
{
$this->app->singleton(AuditLoggerInterface::class, fn ($app) =>
new AuditLogger($app['log']->channel('audit'))
);
}
If your installed version predates 1.1.0 the singleton call above will fail to resolve — check composer show minvws/audit-logger and upgrade before wiring.
First use case worth wiring: a privacy-sensitive read or write — a clinician opening a patient record, an export, a permission change. That is exactly what MinVWS builds these for (NEN 7513 / GDPR accountability), and it validates the whole pipeline end-to-end in one call.
AuditLoggerInterface, never the concrete class. The interface is the whole point of 1.1.0: it lets you decorate, swap in a no-op for tests, and survive a future concrete-class change. Type-hint the interface in constructors everywhere.PatientRecordController@show misses console commands, queued jobs, and API routes. Log inside the action/use-case class that actually performs the operation.terminate() so failed authorizations don't produce misleading "viewed" entries.AuditPatientRepository implements AuditLoggerInterface (or delegates to it) and emits an event on the way through. Keeps audit concerns out of business logic and makes it trivial to swap in a no-op for tests.AuditLoggerInterface in an AppNameAuditLogger that adds app name, environment, and tenant to every context array before delegating. One binding change, zero call-site churn.PatientRecordViewed; a listener translates it into the package's event shape. Your domain never imports the audit package.AuditLoggerInterface (or a collecting PSR-3 LoggerInterface) in the test container and assert on the event name and context array. Do not assert on formatted strings — assert on structured fields.AuditLoggerInterface; the only code change in 1.1.0 is that file (PR #28). Everything else in the release is Dependabot CI bumps — no runtime behaviour changed between 1.0.0 and 1.1.0, so there is no migration risk, only a new abstraction to adopt.^1.1 constraint — an exact 1.1.0 pin is defensible but you want the interface. The API is small enough that vendor-forking is cheap; do it if you need a patch.Object/Array or drop them. Pre-serialize payloads with json_encode(...) before handing them to the logger.date_default_timezone_set('UTC') (or config('app.timezone')) explicitly — mixed-timezone audit trails are a compliance finding waiting to happen.Target [AuditLoggerInterface] is not instantiable — that's your provider, not a package bug.stack: daily rotation will delete records you are legally required to keep for years. Configure a separate channel with its own retention policy.try/catch and make the choice explicit in code and in a comment — auditors will ask. Putting this in your decorator rather than at every call site is the cleanest option.NullLogger or an in-memory logger and dump() the context array — that is faster than chasing formatted output through a handler stack.How can I help you explore Laravel packages today?