polysource / audit
Polysource — audit log for non-Doctrine actions: AuditEntry VO, fan-out logger interface, browsable AuditLogResource, GDPR Art. 30 CSV export, retention command.
Package info
Type:symfony-bundle
pkg:composer/polysource/audit
Requires
- php: >=8.1
- doctrine/dbal: ^3.6 || ^4.0
- doctrine/orm: ^2.20 || ^3.0
- polysource/core: ^0.1
- psr/log: ^1.0 || ^2.0 || ^3.0
- symfony/config: ^5.4 || ^6.0 || ^7.0 || ^8.0
- symfony/dependency-injection: ^5.4 || ^6.0 || ^7.0 || ^8.0
- symfony/event-dispatcher: ^5.4 || ^6.0 || ^7.0 || ^8.0
- symfony/http-foundation: ^5.4 || ^6.0 || ^7.0 || ^8.0
- symfony/http-kernel: ^5.4 || ^6.0 || ^7.0 || ^8.0
- symfony/uid: ^5.4 || ^6.0 || ^7.0 || ^8.0
Requires (Dev)
- easycorp/easyadmin-bundle: ^4.24 || ^5.0
- phpunit/phpunit: ^10.5 || ^11.5
- polysource/symfony-bundle: 0.1.x-dev
- symfony/security-bundle: ^5.4 || ^6.0 || ^7.0 || ^8.0
Suggests
- easycorp/easyadmin-bundle: Audits EasyAdmin CRUD edits (Edit / New / Delete on Doctrine entities) via the bundled EasyAdminAuditSubscriber. Gated on EA being installed; no effect when absent.
This package is auto-updated.
Last update: 2026-05-10 21:02:02 UTC
README
GDPR Art. 30 / HIPAA audit trail for Polysource admin actions.
Part of the Polysource monorepo. MIT-licensed.
When to use
You're running a regulated workload (healthcare, finance, B2B SaaS) and need a write-only, queryable log of every admin action: who did what, when, from where, with what outcome.
What it ships
AuditEntryVO +AuditOutcomeenum +AuditActorInterface(SymfonySecurityAuditActordefault impl).AuditLoggerInterface(write-only) with fan-out viaAggregateAuditLogger.DoctrineAuditLogger+ Doctrine entity (polysource_audit_logtable with 3 indexes for Art. 30 queries).ActionAuditSubscriber— bridgesActionAboutToExecuteEvent/ActionExecutedEvent(dispatched bypolysource/symfony-bundle) to the logger. UUID v7 per entry, IP/UA/RequestID in context, trace truncated to 8KB.EasyAdminAuditSubscriber— optional bridge that audits EA CRUD edits (Edit / New / Delete on Doctrine entities). Auto-wired wheneasycorp/easyadmin-bundleis installed (gated onclass_exists); inert otherwise.AuditLogResource— browsable admin resource with 5 standard filters (time range, actor, outcome, action name, resource).ExportAuditCsvAction— GDPR Art. 30 export with 12 locked columns (RFC 4180), gated onPOLYSOURCE_AUDIT_EXPORT.polysource:audit:purge --before— retention command with cutoff exclusive,--dry-run, exit codes.
See ADR-020.
Install
composer require polysource/audit
polysource/audit declares doctrine/orm and doctrine/dbal as hard runtime dependencies. The bundled persistence layer (DoctrineAuditLogger, AuditEntryRecord, AuditLogDataSource, PurgeAuditCommand, ExportAuditCsvAction) imports Doctrine at compile time, so installing this package without Doctrine would fatal at autoload. If your host already runs Doctrine for any other reason — most Symfony apps do — the cost is zero. If you need the contracts without Doctrine, depend on polysource/core directly and ship a custom AuditLoggerInterface implementation; only the storage layer is Doctrine-coupled.
Register the bundle:
return [ Polysource\Audit\PolysourceAuditBundle::class => ['all' => true], ];
Run the migration to create polysource_audit_log.
Extend it
AuditLoggerInterface is 1 method (log(AuditEntry $entry)). To pipe events to Splunk, Datadog, OpenSearch, or any SIEM:
#[AutoconfigureTag('polysource.audit_logger')] final class SplunkAuditLogger implements AuditLoggerInterface { public function log(AuditEntry $entry): void { $this->splunk->send([ 'time' => $entry->occurredAt->format(\DateTimeInterface::ATOM), 'event' => $entry->actionName, 'actor' => $entry->actorId, 'outcome' => $entry->outcome->value, ]); } }
The AggregateAuditLogger fan-outs across every tagged logger with try/catch isolation — Splunk timing out doesn't break the Doctrine write. See extensibility map.