Enterprise-grade, high-performance audit trail solution for Symfony.
AuditTrailBundle is a modern, lightweight bundle that automatically tracks and stores Doctrine ORM entity changes. Built for performance and compliance, it uses a unique Split-Phase Architecture to ensure your application stays fast even under heavy load.
- High Performance: Non-blocking audits using a Split-Phase Architecture (capture in
onFlush, dispatch inpostFlush). - Multiple Transports:
- Doctrine: Store logs in your local database (default).
- HTTP: Stream logs to external APIs (ELK, Splunk, etc.).
- Queue: Offload processing to Symfony Messenger (RabbitMQ, Redis).
- Deep Collection Tracking: Tracks Many-to-Many and One-to-Many changes with precision (logs exact IDs).
- Sensitive Data Masking: Native support for
#[SensitiveParameter]and custom#[Sensitive]attributes for GDPR compliance. - Safe Revert Support: Easily roll back entities to any point in history, including associations.
- Modern Stack: Built for PHP 8.4+, Symfony 7.4+, and Doctrine ORM 3.0+.
Most Symfony audit solutions either:
- Slow down Doctrine flush operations
- Log incomplete transactional data
- Cannot safely revert changes
- Are hard to extend beyond database storage
AuditTrailBundle is designed for production-grade auditing with:
- Minimal write overhead: Split-phase architecture ensures your app stays fast.
- Transaction-aware logging: Group related changes under a single transaction hash.
- Safe revert support: Easily roll back entities to any point in history, including associations.
- Multiple transport strategies: Store logs in DB, send to an API, or offload to a Queue (Messenger).
This bundle is built using a Split-Phase Audit Architecture to ensure high performance and reliability in Symfony applications.
- Phase 1 (Capture): Listens to Doctrine
onFlushto capture changes without slowing down the transaction. - Phase 2 (Dispatch): Dispatches audits in
postFlushvia your chosen transport.
For a deep dive into the design decisions and how the split-phase approach works, check out the full article on Medium: Designing a Split-Phase Audit Architecture for Symfony
composer require rcsofttech/audit-trail-bundleIf you are using the Doctrine Transport (default), update your database schema:
php bin/console make:migration
php bin/console doctrine:migrations:migrateAdd the #[Auditable] attribute to any Doctrine entity you want to track.
use Rcsofttech\AuditTrailBundle\Attribute\Auditable;
#[ORM\Entity]
#[Auditable(ignoredProperties: ['internalCode'])]
class Product
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
private ?int $id = null;
#[ORM\Column]
private string $name;
#[ORM\Column]
private ?string $internalCode = null;
}Create a configuration file at config/packages/audit_trail.yaml:
audit_trail:
# Enable or disable the bundle globally
enabled: true
# Global list of properties to ignore (e.g., timestamps)
ignored_properties: ['updatedAt', 'updated_at', 'password', 'token']
# Global list of entities to ignore
ignored_entities: []
# Retention period for database logs (in days)
retention_days: 365
# Context tracking
track_ip_address: true
track_user_agent: true
# enable or disable delete tracking
enable_hard_delete: true
enable_soft_delete: true
soft_delete_field: 'deletedAt'
transports:
# Store logs in the local database
doctrine: true
# Send logs to an external API
http:
enabled: false
endpoint: 'https://audit-service.internal/api/logs'
# Dispatch logs to a message queue
queue:
enabled: false
bus: 'messenger.bus.default' # Optional: specify a custom bus
# Transaction Safety & Performance
# --------------------------------
# true (Default): Audits are sent AFTER the transaction commits (postFlush).
# - Pros: High performance, non-blocking. Main transaction succeeds even if audit fails.
# - Cons: Small risk of "data without audit" if app crashes between commit and audit.
# - Recommended for: External transports (HTTP, Queue).
#
# false: Audits are sent DURING the transaction (onFlush).
# - Pros: Strict atomicity. Data and audit are committed together.
# - Cons: Slower. If audit transport fails, the entire transaction rolls back.
# - Recommended for: Doctrine transport (when strict compliance is required).
defer_transport_until_commit: true
# If true, an exception in the transport will stop execution (and rollback if defer=false).
# If false (default), transport errors are logged but execution continues.
fail_on_transport_error: falseYou can ignore specific properties or enable/disable auditing per entity via the class-level attribute.
#[Auditable(enabled: true, ignoredProperties: ['internalCode'])]
class Product
{
private ?string $internalCode = null;
}Sensitive data is automatically masked in audit logs.
Option 1: Use PHP's #[SensitiveParameter]
public function __construct(
#[\SensitiveParameter] private string $password,
) {}Option 2: Use #[Sensitive]
use Rcsofttech\AuditTrailBundle\Attribute\Sensitive;
#[Sensitive(mask: '****')]
private string $ssn;Read and query audit logs programmatically using a dedicated, read-only API.
→ Audit Reader Documentation (Symfony & Doctrine)
The bundle provides several commands for managing audit logs.
php bin/console audit:list --entity=User --action=update --limit=50php bin/console audit:purge --before="30 days ago" --forcephp bin/console audit:export --format=json --output=audits.jsonphp bin/console audit:diff User 42AuditTrailBundle provides a powerful Point-in-Time Restore capability, allowing you to undo accidental changes or recover data from any point in your audit history.
# Revert an entity to its state in a specific audit log
php bin/console audit:revert 123Why it's "Safe":
- Association Awareness: Automatically handles entity relations and collections.
- Soft-Delete Support: Temporarily restores soft-deleted entities to apply the revert.
- Dry Run Mode: Preview exactly what will change before applying (
--dry-run). - Data Integrity: Ensures the entity remains in a valid state after the rollback.
Tip
Use the revert feature for emergency data recovery, undoing unauthorized changes, or restoring accidental deletions with full confidence.
Ensure the integrity of your audit logs by detecting any unauthorized tampering or modifications. This command validates cryptographic hashes to identify compromised records.
php bin/console audit:verify-integrityUse Cases:
- Compliance Audits: Verify that audit logs haven't been altered for regulatory compliance (SOX, HIPAA, GDPR).
- Security Monitoring: Detect unauthorized tampering after security incidents.
- Historical Data Verification: Confirm past records are accurate and trustworthy.
Example Output:
Warning
Any tampered logs indicate a serious security breach. Investigate immediately and review access controls to your database.
Add the AuditLogCrudController to your DashboardController:
use Rcsofttech\AuditTrailBundle\Controller\Admin\AuditLogCrudController;
yield MenuItem::linkToCrud('Audit Logs', 'fas fa-history', AuditLog::class)
->setController(AuditLogCrudController::class);| Operation | Time (mode) | Memory (peak) |
|---|---|---|
| Audit Creation (Overhead) | 1.66ms / flush | 11.25 MB |
| Baseline (Auditing Disabled) | 0.68ms / flush | 10.41 MB |
| Audit Retrieval (10 logs) | 5.60ms | 12.86 MB |
| Audit Purge (1000 logs) | 44.14ms | 21.79 MB |
- PHP 8.4+
- Symfony 7.4+
- Doctrine ORM 3.0+
MIT License.
