routes extension point are introduced in Nuxeo
2025.16. Until then, only the legacy event extension point is available.
The legacy event extension point still works and remains backward-compatible
on later releases.
The AuditRouter is the engine that decides, for each Nuxeo event, which Audit
Backend(s) the resulting LogEntry is written to. It is configured through the
routes extension point of the Audit Service.
How Routing Works
The audit ingestion pipeline goes through three stages:
- The
StreamAuditEventListenerintercepts each audited event, asks theAuditRouterto compute the resultingLogEntryfrom the event context, and the listener appends it to theaudit/auditNuxeo Stream. - The
audit/writercomputation (StreamAuditWriter) consumes theaudit/auditstream entries. - For every entry, the computation calls the
AuditRouterback to get the list of target backends. The router walks the contributed routes and, for every route that matches the entry, returns the configured backend. The entry is then written to each matching backend.
The combination semantics are:
- routes are ORed: a single log entry can be written to several backends if several routes match.
- events of one route are ORed.
- predicates of one route are ORed.
- events AND predicates are combined inside a single route: a route declaring both accepts an entry only when one of its events matches and one of its predicates is satisfied.
A route may declare only events, only predicates, or both:
- Events only — the events declared in the route are registered as auditable events,
so the post-commit listener forwards them to the
audit/auditstream. - Predicates only — the route does not register events; it only filters log entries that already flow through the router (for example, to copy entries flowing to the default backend to a secondary one based on their category).
routes Extension Point
A route is a <route> element with a name, a <backend> and any
combination of <event> and <predicate> children:
<extension target="org.nuxeo.audit.service.AuditComponent" point="routes">
<route name="default">
<backend name="default" />
<event name="documentCreated" />
<event name="documentModified" enabled="true" />
</route>
</extension>
By default the Nuxeo Platform contributes a default route holding all auditable events. Custom contributions to the routes extension point can override or extend this configuration.
<backend>
<backend name="..."/> references a backend registered through the
backendFactory extension point. The platform always contributes a default
backend whose factory comes from nuxeo.audit.backend.default.factory.
<event>
<event name="..." enabled="true|false"/> registers an auditable event.
- The event must be one of the events fired by the platform (or by your code).
- Setting
enabled="false"is the supported way to suppress an event from a route without redefining it; this is how the SQL backend disablessearchby default.
<predicate>
<predicate class="..."/> filters which log entries the route accepts. A
predicate is a java.util.function.Predicate<LogEntry> that takes a
Map<String, String> of <property> entries as constructor argument:
<route name="documentCategory">
<backend name="default" />
<predicate class="org.nuxeo.audit.service.route.CategoryLogEntryPredicate">
<property name="category">eventDocumentCategory</property>
</predicate>
</route>
The platform ships CategoryLogEntryPredicate, which keeps log entries whose
category matches the configured value. You can contribute your own
Predicate<LogEntry> implementation by providing a public constructor that
accepts a Map<String, String>.
Route Inheritance with copy
A route can copy another existing route by referring to it via the copy
attribute. The copying route inherits the backend and the events/predicates
of the source route, and merges them with what it declares locally:
- Locally declared
<event>and<predicate>elements are merged with the inherited ones (entries are identified byname). A local<event name="..." enabled="false"/>disables a specific inherited event. - The
<backend>is replaced when redeclared.
<route name="default-mirrored" copy="default">
<backend name="secondary" />
</route>
Worked Example — Routing a Business Event to a Secondary Backend
The following example contributes a secondary OpenSearch 1.x backend that
receives only the custom hylandBusinessEvent event. The default backend is
left untouched: it keeps receiving the events declared by the implicit
default route, and it does not receive hylandBusinessEvent.
1. Register the secondary backend
<extension target="org.nuxeo.audit.service.AuditComponent" point="backendFactory">
<backend name="secondary" factory="org.nuxeo.audit.opensearch1.OpenSearchAuditBackendFactory" />
</extension>
2. Configure the backend implementation
<extension target="org.nuxeo.audit.opensearch1.OpenSearchAuditBackendFactory" point="backend">
<backend name="secondary"
clientId="audit/secondary"
indexName="${hyland.custom.audit.backend.secondary.opensearch1.index.name:=hyland-business-audit}" />
</extension>
3. Configure the OpenSearch client used by the secondary backend
<extension target="org.nuxeo.runtime.opensearch1.OpenSearchComponent" point="client">
<client id="audit/secondary">
<server>${hyland.custom.audit.backend.secondary.opensearch1.client.server:=http://localhost:9200}</server>
<connectionTimeout>${hyland.custom.audit.backend.secondary.opensearch1.client.connectionTimeout:=30s}</connectionTimeout>
<socketTimeout>${hyland.custom.audit.backend.secondary.opensearch1.client.socketTimeout:=121000ms}</socketTimeout>
<username>${hyland.custom.audit.backend.secondary.opensearch1.client.username:=}</username>
<password>${hyland.custom.audit.backend.secondary.opensearch1.client.password:=}</password>
</client>
</extension>
4. Contribute the route
<extension target="org.nuxeo.audit.service.AuditComponent" point="routes">
<route name="secondary_route">
<backend name="secondary" />
<event name="hylandBusinessEvent" />
</route>
</extension>
From now on, every hylandBusinessEvent raised by the application is ingested
by the secondary OpenSearch backend, while regular events keep flowing to the
default backend.
nuxeo.conf and
disable the other one, for instance:
# default backend is SQL
nuxeo.audit.backend.default.factory=org.nuxeo.audit.sql.SQLAuditBackendFactory
# secondary OpenSearch package must not register itself as default
nuxeo.audit.backend.default.opensearch1.enabled=false