Repository navigation
Home
- Navigation
- Features
- Requirements
- Installation
- Quick Start
- Attributes Reference
- Advanced Usage
- API Reference
- Performance Optimization
- Security Considerations
- Troubleshooting
- Contributing
- License
- Support
- Changelog
- 🔍 Automatic Entity Tracking: Track changes to any Doctrine entity
- 🔄 Rollback Functionality: Restore entities to previous states
- 🎯 Flexible Configuration: Fine-grained control over what gets audited
- 🌐 Web Interface: User-friendly web UI for viewing audit logs
- 🚀 REST API: Complete API for programmatic access
- 🔒 Security Integration: Role-based access control
- 📊 Performance Optimized: Asynchronous processing support
- 🎨 Customizable: Extensible architecture with events
- 📱 Responsive Design: Mobile-friendly interface
- 🔧 Developer Tools: Comprehensive debugging and monitoring
- PHP 7.4 - 8.4
- Symfony 5.4 - 7.x
- Doctrine ORM 2.10+
- MySQL 5.7+ / PostgreSQL 10+ / SQLite 3.25+
composer require benmacha/audit-bundleSymfony Flex will automatically:
- Register the bundle in
config/bundles.php - Create default configuration in
config/packages/audit.yaml - Set up routing in
config/routes/audit.yaml - Configure services for auto-wiring
If you're not using Symfony Flex:
composer require benmacha/audit-bundleThen manually register the bundle in config/bundles.php:
return [
// ...
BenMacha\AuditBundle\AuditBundle::class => ['all' => true],
];composer require benmacha/audit-bundleAdd the bundle to your config/bundles.php:
<?php
return [
// ...
BenMacha\AuditBundle\AuditBundle::class => ['all' => true],
];With Symfony Flex, the configuration file config/packages/audit.yaml is automatically created with sensible defaults:
audit:
# Enable/disable the audit system
enabled: '%env(bool:AUDIT_ENABLED)%'
# Storage configuration
storage:
driver: '%env(AUDIT_STORAGE_DRIVER)%'
# Logging configuration
logging:
level: '%env(AUDIT_LOG_LEVEL)%'
# Default audit configuration
entities:
# Auto-discover entities with Auditable attribute
auto_discover: true
# Default settings for audited entities
defaults:
track_created_at: true
track_updated_at: true
track_deleted_at: true
store_old_values: true
store_new_values: true
# Web interface configuration
web_interface:
enabled: true
route_prefix: '/audit'
# API configuration
api:
enabled: true
route_prefix: '/api/audit'
# Security configuration
security:
# Roles required to access audit features
view_role: 'ROLE_ADMIN'
manage_role: 'ROLE_SUPER_ADMIN'
rollback_role: 'ROLE_SUPER_ADMIN'
# Performance settings
performance:
batch_size: 100
cache_enabled: true
cache_ttl: 3600Add these environment variables to your .env file:
# Audit Bundle Configuration
AUDIT_ENABLED=true
AUDIT_STORAGE_DRIVER=doctrine
AUDIT_LOG_LEVEL=infoCreate config/packages/audit.yaml:
audit:
enabled: true
retention_days: 365
async_processing: false
# Database configuration
database:
connection: default
# Entity-specific configuration
entities:
App\Entity\User:
enabled: true
operations: ['create', 'update', 'delete']
ignored_fields: ['password', 'lastLogin']
App\Entity\Product:
enabled: true
operations: ['create', 'update']
# API configuration
api:
enabled: true
rate_limit: 100
prefix: '/api/audit'
# Security roles
security:
admin_role: 'ROLE_ADMIN'
auditor_role: 'ROLE_AUDITOR'
developer_role: 'ROLE_DEVELOPER'
# UI configuration
ui:
route_prefix: '/admin/audit'
items_per_page: 25
show_ip_address: truephp bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrateAdd to config/routes.yaml:
audit_bundle:
resource: '@AuditBundle/Resources/config/routes.yaml'
prefix: /admin
audit_api:
resource: '@AuditBundle/Resources/config/api_routes.yaml'
prefix: /apiSimply add the #[Auditable] attribute to your entity:
<?php
namespace App\Entity;
use BenMacha\AuditBundle\Attribute\Auditable;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[Auditable]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private int $id;
#[ORM\Column(type: 'string')]
private string $username;
#[ORM\Column(type: 'string')]
private string $email;
// ... getters and setters
}Access the web interface at /admin/audit or use the API:
# Get all audit logs
curl -X GET /api/audit/logs
# Get logs for specific entity
curl -X GET /api/audit/logs/entity/User/123
# Rollback entity to previous state
curl -X POST /api/audit/rollback/456Marks an entity as auditable.
use BenMacha\AuditBundle\Attribute\Auditable;
#[Auditable(
operations: ['create', 'update', 'delete'], // Operations to track
ignoredFields: ['password', 'updatedAt'], // Fields to ignore
async: true // Process asynchronously
)]
class User
{
// ...
}Parameters:
-
operations: Array of operations to track (create,update,delete) -
ignoredFields: Array of field names to exclude from auditing -
async: Whether to process audit logs asynchronously
Excludes specific fields or operations from auditing.
use BenMacha\AuditBundle\Attribute\IgnoreAudit;
class User
{
#[IgnoreAudit] // Never audit this field
private string $password;
#[IgnoreAudit(operations: ['update'])] // Don't audit on updates
private \DateTime $createdAt;
}Parameters:
-
operations: Array of operations to ignore for this field
Marks fields as sensitive for special handling.
use BenMacha\AuditBundle\Attribute\AuditSensitive;
class User
{
#[AuditSensitive(
encrypt: true, // Encrypt the value
mask: true, // Mask in UI (show as ***)
hashAlgorithm: 'sha256' // Hash algorithm for encryption
)]
private string $socialSecurityNumber;
}Parameters:
-
encrypt: Whether to encrypt the field value -
mask: Whether to mask the value in the UI -
hashAlgorithm: Algorithm for hashing sensitive data
Adds custom metadata to audit logs.
use BenMacha\AuditBundle\Attribute\AuditMetadata;
class User
{
#[AuditMetadata(
tags: ['pii', 'gdpr'], // Custom tags
indexed: true, // Index for searching
ttl: 2592000, // TTL in seconds (30 days)
customData: ['department' => 'HR'] // Additional metadata
)]
private string $email;
}Parameters:
-
tags: Array of custom tags for categorization -
indexed: Whether to index this field for searching -
ttl: Time-to-live in seconds -
customData: Additional custom metadata
Provides additional context for audit operations.
use BenMacha\AuditBundle\Attribute\AuditContext;
#[AuditContext(
reason: 'User profile update',
category: 'user_management',
priority: 'high',
metadata: ['source' => 'admin_panel']
)]
class User
{
// ...
}Parameters:
-
reason: Human-readable reason for the change -
category: Category for grouping related changes -
priority: Priority level (low,medium,high,critical) -
metadata: Additional context metadata
Listen to audit events for custom processing:
<?php
namespace App\EventSubscriber;
use BenMacha\AuditBundle\Event\AuditEvent;
use BenMacha\AuditBundle\Event\AuditEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class AuditSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
AuditEvents::PRE_AUDIT => 'onPreAudit',
AuditEvents::POST_AUDIT => 'onPostAudit',
AuditEvents::ROLLBACK => 'onRollback',
];
}
public function onPreAudit(AuditEvent $event): void
{
// Modify audit data before saving
$auditLog = $event->getAuditLog();
$auditLog->addMetadata('processed_by', 'custom_subscriber');
}
public function onPostAudit(AuditEvent $event): void
{
// React to audit log creation
$this->notificationService->sendAuditNotification($event->getAuditLog());
}
public function onRollback(AuditEvent $event): void
{
// Handle rollback operations
$this->logger->info('Entity rolled back', [
'entity' => $event->getEntityClass(),
'id' => $event->getEntityId()
]);
}
}Log changes manually when needed:
<?php
namespace App\Service;
use BenMacha\AuditBundle\Service\AuditService;
class UserService
{
public function __construct(
private AuditService $auditService
) {}
public function updateUserProfile(User $user, array $data): void
{
$oldData = $this->extractUserData($user);
// Update user
$user->setEmail($data['email']);
$user->setName($data['name']);
// Manual audit logging
$this->auditService->logEntityChange(
$user,
'update',
$oldData,
$this->extractUserData($user),
'Profile updated via API'
);
}
}Implement custom rollback behavior:
<?php
namespace App\Service;
use BenMacha\AuditBundle\Service\AuditService;
use BenMacha\AuditBundle\Entity\AuditLog;
class CustomRollbackService
{
public function __construct(
private AuditService $auditService
) {}
public function rollbackWithValidation(AuditLog $auditLog): bool
{
// Custom validation logic
if (!$this->canRollback($auditLog)) {
throw new \Exception('Rollback not allowed');
}
// Perform rollback
return $this->auditService->rollbackEntity($auditLog);
}
private function canRollback(AuditLog $auditLog): bool
{
// Implement your validation logic
return $auditLog->getCreatedAt() > new \DateTime('-24 hours');
}
}Manage audit data lifecycle:
<?php
namespace App\Command;
use BenMacha\AuditBundle\Service\AuditCleanupService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class AuditCleanupCommand extends Command
{
protected static $defaultName = 'audit:cleanup';
public function __construct(
private AuditCleanupService $cleanupService
) {
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
// Clean up old audit logs
$deleted = $this->cleanupService->cleanupExpiredLogs();
$output->writeln("Deleted {$deleted} expired audit logs");
// Optimize audit tables
$this->cleanupService->optimizeTables();
$output->writeln('Optimized audit tables');
return Command::SUCCESS;
}
}Override default templates:
- Create
templates/bundles/AuditBundle/directory - Copy templates from
vendor/benmacha/audit-bundle/src/Resources/views/ - Customize as needed
{# templates/bundles/AuditBundle/audit/index.html.twig #}
{% extends 'base.html.twig' %}
{% block title %}Custom Audit Logs{% endblock %}
{% block body %}
<div class="custom-audit-container">
<h1>My Custom Audit Interface</h1>
{# Include original content with modifications #}
{% include '@Audit/audit/_table.html.twig' %}
</div>
{% endblock %}| Method | Endpoint | Description |
|---|---|---|
| GET | /api/audit/logs |
Get all audit logs |
| GET | /api/audit/logs/{id} |
Get specific audit log |
| GET | /api/audit/logs/entity/{class}/{id} |
Get logs for entity |
| POST | /api/audit/rollback/{id} |
Rollback to audit log |
| DELETE | /api/audit/logs/{id} |
Delete audit log |
| GET | /api/audit/stats |
Get audit statistics |
-
limit: Number of results (default: 25) -
offset: Offset for pagination (default: 0) -
entity: Filter by entity class -
operation: Filter by operation type -
user: Filter by user ID -
from: Start date (ISO 8601) -
to: End date (ISO 8601)
{
"data": [
{
"id": 123,
"entityClass": "App\\Entity\\User",
"entityId": "456",
"operation": "update",
"oldValues": {"email": "old@example.com"},
"newValues": {"email": "new@example.com"},
"userId": 789,
"ipAddress": "192.168.1.1",
"userAgent": "Mozilla/5.0...",
"createdAt": "2024-01-15T10:30:00Z",
"metadata": {"reason": "Profile update"}
}
],
"total": 1,
"limit": 25,
"offset": 0
}Enable async processing for high-traffic applications:
# config/packages/audit.yaml
audit:
async_processing: true
# config/packages/messenger.yaml
framework:
messenger:
transports:
audit: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'BenMacha\AuditBundle\Message\AuditMessage': audit- Indexing: The bundle automatically creates optimized indexes
- Partitioning: Consider table partitioning for large datasets
- Archiving: Use the cleanup service to archive old data
// Batch processing for large operations
$batchSize = 100;
for ($i = 0; $i < $totalRecords; $i += $batchSize) {
$entities = $repository->findBy([], null, $batchSize, $i);
foreach ($entities as $entity) {
// Process entity
}
$entityManager->flush();
$entityManager->clear(); // Clear memory
}# config/packages/security.yaml
security:
access_control:
- { path: ^/admin/audit, roles: ROLE_AUDITOR }
- { path: ^/api/audit, roles: ROLE_API_USER }-
Encryption: Use
#[AuditSensitive]for sensitive fields - Masking: Hide sensitive data in UI
- Retention: Configure appropriate retention periods
- Access Logging: All access is automatically logged
-
Missing Audit Logs
- Check entity has
#[Auditable]attribute - Verify bundle is enabled
- Check database permissions
- Check entity has
-
Performance Issues
- Enable async processing
- Optimize database indexes
- Reduce retention period
-
Memory Issues
- Use batch processing
- Clear entity manager regularly
- Increase PHP memory limit
Enable debug logging:
# config/packages/dev/monolog.yaml
monolog:
handlers:
audit:
type: stream
path: '%kernel.logs_dir%/audit.log'
level: debug
channels: ['audit']- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
git clone https://github.com/benmacha/audit-bundle.git
cd audit-bundle
composer install
php bin/phpunitcomposer run quality # Run all quality checks
composer run cs-fix # Fix coding standards
composer run phpstan # Static analysis
composer run psalm # Additional static analysisThis bundle is released under the MIT License. See LICENSE for details.
See CHANGELOG.md for version history.
Symfony Audit Bundle | GitHub Repository | Issues | Discussions
Last updated: 2025-08-27 14:00:41 UTC