The Audit Service is used for logging and retrieving audit data into a data store. The service can be accessed directly with the Java API for reading or writing audit entries but the main source for Audit entries is the Nuxeo event bus: the Audit Service listens to all events that may occur on the platform (document creation, user logging in, workflow started ...) and according to the configuration an Audit record will be created.
Architecture
The Audit Service is mainly a data store service. It defines a data record structure that will be used for storing audit information.
The data record structure is defined in Java by the LogEntry and
ExtendedInfo Java classes. The Audit Service receives events from the Event
Service. Then the Audit Service filters and converts them into log entries. The
LogEntry class is mainly obtained from a DocumentEventContext.
Nuxeo documents and events can have a lot of custom properties, so if you want
to log some specific events or document properties, the
Extended Info allows for a Key/Value type storage that will be
associated to the main LogEntry record. This information is extracted from
the event message using an EL (Expression Language) expression and stored into
a map.
Since LTS 2025 the Audit Service has been redesigned to support multiple
backends in parallel through the
AuditRouter. The high-level pipeline is:
Nuxeo Event Service
│
▼
StreamAuditEventListener (sync, priority 500)
│ AuditRouter computes LogEntry from event
▼
audit/audit Nuxeo Stream
│
▼
audit/writer computation (StreamAuditWriter)
│ AuditRouter.routeToBackend(LogEntry)
▼
├──► Audit Backend "default"
├──► Audit Backend "secondary"
└──► …
Services
| Service | Purpose |
|---|---|
org.nuxeo.audit.service.AuditService |
Entry point — exposes the configured backends and the router. |
org.nuxeo.audit.service.AuditBackend |
Backend SPI — read, write and query log entries. |
org.nuxeo.audit.service.AuditRouter |
Internal service computing and routing log entries to the configured backends. |
The legacy AuditReader, AuditLogger and AuditAdmin services from the
org.nuxeo.ecm.platform.audit.api package are still available and proxy to the
default backend. They are deprecated and will be removed in a future release —
new code should target AuditService and AuditBackend directly. To get the
default backend:
AuditService auditService = Framework.getService(AuditService.class);
AuditBackend backend = auditService.getAuditBackend("default");
See the How to Upgrade Nuxeo Audit Service page for the full migration guide.
Audit Backends
Since LTS 2025, you have to explicitly choose a backend implementation from the following implementations listed below:
| Backend | Marketplace Package | Reference |
|---|---|---|
| In-Memory (not for production) | Built-in Nuxeo Server (default) | — |
| OpenSearch 1.x / Elasticsearch 7.x - 8.x | nuxeo-audit-opensearch1 | OpenSearch 1.x Audit Backend |
| OpenSearch 2.x | nuxeo-audit-opensearch2 | OpenSearch 2.x Audit Backend |
| Elasticsearch 9.x | nuxeo-audit-elasticsearch9 | Elasticsearch 9.x Audit Backend |
| MongoDB | nuxeo-audit-mongodb | MongoDB Audit Backend |
| SQL Database (Legacy) | nuxeo-audit-sql | SQL Audit Backend (Legacy) |
Each child page lists the configuration properties exposed by the package as
nuxeo.audit.backend.default.<implementation>.* so you can override them in
nuxeo.conf.
Audit Router
The Audit Router (since 2025.16) replaces the
legacy event extension point with a richer routes extension point:
- A route binds a backend to a set of events and predicates.
- Several routes can match the same entry, so the same event can be written to multiple backends in parallel.
- The legacy
eventextension point still works and is automatically merged into the implicitdefaultroute.
The Audit Router child page contains the full extension-point reference and worked examples (route a custom business event to a secondary backend, filter by category, etc.).
Querying the Audit Data Store
The AuditService is the entry point to read audit data. From it you get an
AuditBackend that exposes the read API on a specific backend (the default
one in most cases).
A set of methods allows you to do common queries quite easily like getting all the log entries for a document, getting a specific log by its id, etc.
AuditService auditService = Framework.getService(AuditService.class);
AuditBackend backend = auditService.getAuditBackend("default");
// Getting of the logs for the document 'doc' in 'myRepository'
List<LogEntry> logEntries = backend.getLogEntriesFor(doc.getId(), "myRepository");
// Same method but with a query builder
AuditQueryBuilder builder = new AuditQueryBuilder();
builder.predicates(Predicates.eq(LogEntryConstants.LOG_DOC_UUID, doc.getId()),
Predicates.eq(LogEntryConstants.LOG_REPOSITORY_ID, "myRepository"));
List<LogEntry> logEntriesFiltered = backend.queryLogs(builder);
To target another backend, just ask the AuditService for it:
AuditBackend secondary = auditService.getAuditBackend("secondary");
List<LogEntry> entries = secondary.queryLogs(builder);
There are two PageProviders that can be used for querying the Audit data store:
AuditPageProvider: allows to generate simple queries against Audit entries.DocumentHistoryReader: allows to compute history for a given document.
By default both PageProviders query the default backend. They support targeting
a specific backend through the backend PageProvider property.
A schema has been defined for basic Audit search: basicauditsearch.xsd. This
schema is helpful for building a PageProvider querying the Audit data store. An
object BasicAuditSearch could be used to define queries on the audit data
store.
Extending the Audit Service
The Audit Service exposes the following extension points on the
org.nuxeo.audit.service.AuditComponent component:
| Extension Point | Purpose |
|---|---|
routes |
Bind a backend to a set of events and predicates. Since 2025.16. |
event |
Register an auditable event. Deprecated since 2025.16 — use routes instead. Backward-compatible. |
extendedInfo |
Extract custom values from the event context using EL and store them on the log entry. |
adapter |
Register an adapter exposed to EL expressions of extendedInfo. |
backendFactory |
Register an AuditBackend implementation. Contributed by the audit Marketplace Packages, rarely used by integrations. |
Routes — Recommended Since 2025.16
The routes extension point is the modern way to declare what is audited and
where it is stored. A minimal route registers an event and writes it to the
default backend:
<extension target="org.nuxeo.audit.service.AuditComponent" point="routes">
<route name="documentLifecycle">
<backend name="default" />
<event name="documentCreated" />
<event name="documentModified" />
</route>
</extension>
See the Audit Router child page for the full
reference (predicates, route inheritance with copy, routing semantics, …) and
worked examples.
The Nuxeo Platform contributes a default route which holds the auditable
events matching the Nuxeo core base events:
documentCreateddocumentCreatedByCopydocumentDuplicateddocumentMoveddocumentRemoveddocumentModifieddocumentLockeddocumentUnlockeddocumentSecurityUpdatedlifecycle_transition_eventloginSuccessloginFailedlogoutdocumentCheckedInversionRemoveddocumentProxyPublishedsectionContentPublisheddocumentRestored
If you are sending new Nuxeo core events and want them to be audited, you have
to contribute them to the routes extension point. Here is an example of a
contribution adding a custom event to the default route:
<extension target="org.nuxeo.audit.service.AuditComponent" point="routes">
<route name="default">
<event name="myCustomEvent" />
</route>
</extension>
Event — Deprecated Since 2025.16
The event extension point keeps registering auditable events on the implicit
default route. It is the legacy way of declaring audited events and
remains supported for backward compatibility.
Extended Info
This service is used to evaluate EL expressions using a document as context and registering results into a Map indexed by names.
Just after converting a received DocumentEventContext instance into the
corresponding LogEntry instance, the Audit Service allows you to extract
information from the handling context and to store them.
To do this, you have to define an EL expression and associate it with a key. You can access to the following variables:
message: Document event context describing the event.source: Document from which the event is from.principal: Identity of the event owner.
If you want to contribute to the extended info of the service, you have to use
the extendedInfo extension point. Here is an example of a contribution to this
extension point
<extension target="org.nuxeo.audit.service.AuditComponent" point="extendedInfo">
<extendedInfo expression="${source.dublincore.title}" key="title" />
<extendedInfo expression="${message.cacheKey}" key="key" />
<extendedInfo expression="${principal.name}" key="user" />
</extension>
You can also extend the audit info per event name:
<extension target="org.nuxeo.audit.service.AuditComponent" point="event">
<event name="afterWorkflowStarted">
<extendedInfos>
<extendedInfo expression="${message.properties.modelId}" key="modelId" />
<extendedInfo expression="${message.properties.modelName}" key="modelName" />
<extendedInfo expression="${message.properties.workflowInitiator}" key="workflowInitiator" />
<extendedInfo expression="${message.properties.workflowVariables}" key="workflowVariables" />
</extendedInfos>
</event>
</extension>
For instance, the above contribution will add modelId, modelName,
workflowInitiator, workflowVariables to the extendedInfo only for the
afterWorkflowStarted event.
extendedInfo entries have to be declared directly on the
extendedInfo extension point with an event attribute, for instance:
<extension target="org.nuxeo.audit.service.AuditComponent" point="extendedInfo">
<extendedInfo event="afterWorkflowStarted" expression="${message.properties.modelId}" key="modelId" />
</extension>
This is the recommended form. The legacy form nesting <extendedInfo> inside
<event> is kept for backward compatibility but will be removed in the future.
Adapter
The contribution to the adapter extension point of the
org.nuxeo.audit.service.AuditComponent component allows to define the adapter
that will be injected in the EL context. Here is an example of a contribution to
this extension point.
<extension target="org.nuxeo.audit.service.AuditComponent" point="adapter">
<adapter name="myadapter" class="org.nuxeo.ecm.core.api.facet.VersioningDocument"/>
</extension>
Live Ingestion — Stream Audit Writer
The audit ingestion pipeline is built on top of
Nuxeo Stream. A synchronous event listener
(StreamAuditEventListener) serializes each audited event as a LogEntry and
appends it to the audit/audit stream. A computation (auditWriter) consumes
the stream and delegates the actual write to the
AuditRouter.
This replaces the historical post-commit auditLoggerListener and the dedicated
Works audit queue: there is no queue extension point to configure for
the Audit Service anymore.