Create a configuration file at config/packages/audit_trail.yaml.
For detailed transport configuration and usage, see Audit Transports.
audit_trail:
# Enable or disable the bundle globally
enabled: true
# Global list of properties to ignore
# Defaults to common timestamp fields only
ignored_properties: ['updatedAt', 'updated_at']
# Optional prefix/suffix for the audit table name.
# Empty values are allowed.
# Non-empty values may contain only letters, numbers, and underscores
# and must not start with a digit.
table_prefix: ''
table_suffix: ''
# 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'
# Attempt database-backed fallback persistence if another transport fails.
# This uses the bundle's phase-aware fallback path and may persist immediately
# or defer safely depending on the current audit phase.
fallback_to_database: true
# Optional cache pool used for cross-request access-audit cooldowns.
# If null, request-level deduplication still works, but cooldowns are not
# persisted across requests.
cache_pool: null
# Required role/permission for EasyAdmin audit actions
admin_permission: 'ROLE_ADMIN'
# HTTP methods eligible for access auditing
audited_methods: ['GET']
# Collection Serialization Performance
# -----------------------------------
collection_serialization_mode: 'lazy'
max_collection_items: 100
transports:
# Store logs in the local database
database:
enabled: true
async: false # Set to true to persist via Messenger worker (requires symfony/messenger and an 'audit_trail_database' transport)
# Send logs to an external API
http:
enabled: false
endpoint: 'https://audit-service.internal/api/logs'
headers: { }
timeout: 5
# Dispatch logs to a message queue
queue:
enabled: false
bus: null # Optional: specify a custom bus
api_key: null
# Integrity & Signing
# -------------------
# Enable cryptographic signing of audit logs to prevent tampering.
integrity:
enabled: false
secret: '%env(AUDIT_INTEGRITY_SECRET)%'
algorithm: 'sha256'
# Transaction Safety & Performance
# --------------------------------
# true (Default): Audits are delivered after the Doctrine postFlush boundary.
# - Pros: High performance, non-blocking. Main flush succeeds even if deferred audit delivery fails.
# - Pros: Avoids nested Doctrine flushes; deferred database writes use a dedicated writer path.
# - Cons: Small risk of "data without audit" if the process fails after the main flush and before delivery completes.
# - Recommended for: External transports (HTTP, Queue), or database transport in default deferred mode.
#
# false: Eligible transports may be attempted during onFlush.
# - Pros: Strict atomicity. Data and audit are committed together.
# - Cons: Slower. If an in-transaction transport fails, the entire transaction rolls back.
# - Note: Transport support is phase-specific. HTTP and queue transports still
# run in deferred phases such as postFlush/postLoad even when this is false.
# - 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: false- At least one transport must be enabled when
audit_trail.enabledistrue - The database transport is enabled by default
- Enabling
transports.database.asyncortransports.queuewithoutsymfony/messengerinstalled throws a clearLogicException - Enabling
transports.httpwithoutsymfony/http-clientinstalled throws a clearLogicException integrity.secretis required only whenintegrity.enabledistruehttp.endpointmust start withhttp://orhttps://when HTTP transport is enabledtable_prefixandtable_suffixmust be strings; non-empty values may contain only letters, numbers, and underscores and must not start with a digitmax_collection_itemsmust be at least1- If
cache_poolisnull, access-audit cooldowns are request-local only; cross-request cooldown persistence is disabled
Install additional packages only for the features you enable:
- Synchronous database transport: no extra package required
transports.database.async: true: installsymfony/messengertransports.queue.enabled: true: installsymfony/messengertransports.http.enabled: true: installsymfony/http-client- EasyAdmin UI: install
easycorp/easyadmin-bundleand enableEasyAdminBundle
These three options control the bundle's failure boundary:
| Option | Default | What It Changes |
|---|---|---|
defer_transport_until_commit |
true |
Delivers audits after the Doctrine postFlush boundary instead of during onFlush. |
fail_on_transport_error |
false |
Escalates transport exceptions instead of logging and continuing. |
fallback_to_database |
true |
Attempts phase-aware database-backed fallback persistence when another transport fails. |
| Goal | Recommended Settings | Result |
|---|---|---|
| Best default safety/performance | defer_transport_until_commit: true, fail_on_transport_error: false |
Main writes succeed even if HTTP/queue delivery fails after the main flush. |
| Strict in-transaction auditing | defer_transport_until_commit: false, fail_on_transport_error: true, database.enabled: true, database.async: false |
For the synchronous database transport path, data and audit fail together when the audit cannot be recorded safely. |
| External transport with local safety net | defer_transport_until_commit: false, fail_on_transport_error: false, fallback_to_database: true, database.enabled: true |
External transport failures can still be captured locally without requiring a nested flush() during onFlush. |
- When
defer_transport_until_commitisfalse, the bundle still avoids callingflush()from inside DoctrineonFlush. - Transport support remains phase-specific even when
defer_transport_until_commitisfalse. For example, HTTP and queue delivery still occur in deferred phases rather than inside the Doctrine transaction, so this setting does not make every enabled transport part of the same transactional boundary. - If fallback is needed during
onFlush, the database audit entity is attached through DoctrineUnitOfWorkchange-set computation and joins the application's existing flush. - If
defer_transport_until_commitistrue, there is a small but real window where the main transaction can commit and the audit delivery can fail afterward. This is the default performance trade-off for HTTP and queue transports. - In deferred database mode, the bundle no longer performs a follow-up ORM
flush()frompostFlush. DeferredAuditLogrows are written through a dedicated database writer instead. - Because deferred database writes use a dedicated writer, Doctrine ORM lifecycle callbacks/listeners on
AuditLogare not involved in that deferred path. - When
fallback_to_databaseis enabled, the dispatcher uses the bundle's own phase-aware fallback persistence path. OnonFlushit joins the currentUnitOfWork; on deferred and manual phases it writes through the dedicated database writer; and on failure it logs the fallback failure explicitly.
Choose the mode that best fits your performance and audit requirements:
| Mode | Database Impact | Audit Detail | Recommended Use Case |
|---|---|---|---|
lazy (Default) |
None for uninitialized collections | Low | Returns an uninitialized placeholder for lazy Doctrine collections. |
ids_only |
Low | Medium to High | Serializes collection members as identifiers when IDs are available. |
eager |
Medium | High | Initializes lazy Doctrine collections before serializing them. |
If a collection exceeds max_collection_items, the stored payload is truncated to:
{
"_truncated": true,
"_total_count": 250,
"_sample": [1, 2, 3]
}