Recover, export, verify, and consume audit logs.
Use the bundle's recovery tooling, CLI commands, and stable JSON payload shape to make audit data useful after it has been written.
Revert & Recovery
AuditTrailBundle can revert an entity to an earlier audited state. Use it after reviewing the history for a bad import, accidental update, mistaken delete, or failed admin action.
# Revert an entity to its state in a specific audit log
php bin/console audit:revert <audit-log-uuid>
# Add recovery context
php bin/console audit:revert <audit-log-uuid> \
--context='{"reason":"restore customer address","ticket":"SUPPORT-1234"}'
# Preview first when recovering important data
php bin/console audit:revert <audit-log-uuid> --dry-run
Revert In Code
<?php
declare(strict_types=1);
$auditReverter->revert($log, dryRun: false, force: false, context: [
'reason' => 'restore customer address',
'ticket_id' => 'SUPPORT-1234',
]);
By default, AuditReverter silences the normal subscriber while it writes the
explicit revert audit. This avoids a duplicate technical update log.
Pass silenceSubscriber: false when strict compliance or debugging requires the
normal technical log too.
$auditReverter->revert(
$log,
dryRun: false,
force: false,
context: [],
silenceSubscriber: false,
);
- EasyAdmin shows the revert button only for the latest meaningful state-changing log.
- The explicit revert audit points back to the original entry through
reverted_log_id. - Soft-delete restore clears the configured nullable timestamp-like
soft_delete_field. - Use custom revert handlers for action-specific restore logic.
Revert audits follow the same transport rules as other audit logs. In-transaction transports can
roll back with the entity revert when fail_on_transport_error: true. Deferred-only
transports such as HTTP and queue run only after the entity change commits successfully.
Custom Revert Handlers
Implement RevertActionHandlerInterface for action-specific restore logic.
Implementations are auto-tagged when Symfony autoconfiguration is enabled, so you normally do
not need to add the audit_trail.revert_action_handler tag manually.
EasyAdmin uses the same dry-run path for its revert preview. To-many relations are shown as readable collections, UUID-backed relations are previewed safely, and inverse-side collection restores synchronize owning relations so persisted reverts match the preview more reliably.
CLI Commands
List Audit Logs
php bin/console audit:list --entity=User --action=update --limit=50
Purge Old Logs
php bin/console audit:purge --before="30 days ago" --force
If integrity signing is enabled, purge verifies matching logs before deletion unless you pass
--skip-integrity. The purge verification path streams matching rows instead of
eager-loading large result sets.
Export Logs
php bin/console audit:export --format=json --output=audits.json
Use CLI export for large or full-history exports. EasyAdmin export applies active filters and is
capped by easyadmin.export_limit.
View Diff
# By entity class and ID
php bin/console audit:diff 'App\Entity\User' 42
# Or by audit log UUID
php bin/console audit:diff <audit-log-uuid> --json
Verify Integrity
# Verify all logs
php bin/console audit:verify-integrity
# Verify one log
php bin/console audit:verify-integrity --id=<audit-log-uuid>
Useful Options
| Command | Options Developers Commonly Need |
|---|---|
audit:list |
--entity, --entity-id, --user,
--transaction, --from, --to,
--details |
audit:export |
--format=json|csv, --output, --limit,
--entity, --action |
audit:revert |
--dry-run, --force, --user,
--context, --noisy |
audit:purge |
--dry-run, --before, --force,
--skip-integrity |
For console-triggered audits, IP handling is conservative. The bundle prefers valid values such
as AUDIT_TRAIL_CLI_IP, SSH_CLIENT, or
SSH_CONNECTION; if no valid IP is available, it stores null.
Serialization
AuditLogMessageSerializer emits a flat JSON payload so queue or HTTP consumers can
process audit logs outside the Symfony application.
{
"entity_class": "App\\Entity\\User",
"entity_id": "123",
"action": "update",
"old_values": { "status": "pending" },
"new_values": { "status": "approved" },
"changed_fields": ["status"],
"user_id": "42",
"username": "admin",
"ip_address": "127.0.0.1",
"user_agent": "Mozilla/5.0",
"transaction_hash": "019e05e8-e794-7797-bf4f-8286f65a18cd",
"created_at": "2024-01-20T12:00:00+00:00",
"signature": "entity-hmac-signature-here",
"delivery_id": null,
"reverted_log_id": null,
"context": { "source": "checkout" }
}
The serializer is encode-only. Consumers should treat the payload as plain JSON and read
transport-level signatures from HTTP headers or Messenger stamps. Queue delivery can carry
X-Audit-Api-Key and X-Audit-Signature headers when the matching
stamps are present; HTTP delivery uses the X-Signature header when integrity is
enabled.