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. These informations are extracted from the event message using and EL (Expression Language) expression and stored into a map.
By default, since Nuxeo LTS 2015, the data store relies on the Elasticsearch Back-end. To disable Elasticsearch for Audit logs and use the Legacy SQL Back-end please refer to the Disabling Elasticsearch for Audit Logs section.
Elasticsearch Back-end
The audit entries are stored in the Elasticsearch index named by the audit.elasticsearch.indexName
property in nuxeo.conf
.
Make sure you read the Backing Up and Restoring the Audit Elasticsearch Index section.
Fore more information about the global Elasticsearch setup, see Elasticsearch Setup.
Legacy SQL Back-end
If Elasticsearch is disabled for Audit logs, the data store is built over a relational database back-end.
The LogEntry
and ExtendedInfo
Java classes are mapped onto the datastore using JPA (Java Persistence API) annotations.
There are three tables used by the Audit Service: NXP_LOGS
, NXP_LOGS_EXTINFO
and NXP_LOGS_MAPEXTINFOS
. NXP_LOGS
is the main table, it is used most of the time. The two others are used only when the extendedInfo
extension point is defined.
MongoDB Back-end
The audit entries can also be stored in a MongoDB database. The entries will be stored in the audit
collection by default. To enable the MongoDB data store in place of the Elasticsearch or SQL ones, activate the mongodb-audit
template in nuxeo.conf
.
Querying the Audit Data Store
The Service API is composed of three services:
AuditReader
: service for reading data from the audit logs. More details.AuditLogger
: service for adding data into the audit logs. More details.AuditAdmin
: service for administrating the Audit Service.
A set of methods allows the user to do common queries quite easily like getting all the log entries for a document, getting a specific log by its id, etc.
AuditReader reader = Framework.getService(AuditReader.class);
// Getting of the logs for the document 'doc' in 'myRepository'
List<LogEntry> logEntries = reader.getLogEntriesFor(doc.getId(), "myRepository");
// Same method but with a query builder
AuditQueryBuilder builder = new AuditQueryBuilder();
Predicate docIdAndRepo = Predicates.and(Predicates.eq("docUUID", doc.getId()), Predicates.eq("repositoryId", "myRepository"));
builder.predicate(docIdAndRepo);
List<LogEntry> logEntriesFiltered = reader.queryLogs(builder);
You can perform some simple queries using the Elasticsearch API, here is an example of getting all the logs of the category 'MyExport' ordered by the date of the event:
List<LogEntry> entries = new ArrayList<>();
ElasticSearchAdmin esa = Framework.getService(ElasticSearchAdmin.class);
SearchRequestBuilder builder = esa.getClient()
.prepareSearch(esa.getIndexNameForType(ElasticSearchConstants.ENTRY_TYPE))
.setTypes(ElasticSearchConstants.ENTRY_TYPE)
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
builder.setQuery(QueryBuilders.termQuery("category", "MyExport"));
builder.addSort("eventDate", SortOrder.DESC);
SearchResponse searchResponse = builder.execute().actionGet();
for (SearchHit hit : searchResponse.getHits()) {
try {
entries.add(AuditEntryJSONReader.read(hit.getSourceAsString()));
} catch (IOException e) {
log.error("Error while reading Audit Entry from ES", e);
}
}
When using the legacy SQL back-end, you can use AuditReader
to do simple queries using the JPA Query language:
StringBuffer query = new StringBuffer("from LogEntry log where ");
query.append(" log.category='");
query.append("MyExport");
query.append("' ORDER BY log.eventDate DESC");
AuditReader reader = Framework.getService(AuditReader.class);
List<LogEntry> result = (List<LogEntry>)reader.nativeQuery(query.toString(), 1, 1);
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.
A schema has been defined for basic Audit search: basicauditsearch.xsd
. This schema is helpful for building a PageProvider feeding a ContentView with data from the Audit data store. An object BasicAuditSearch
could be used to define queries on the audit data store.
Extending the Audit Service
There a few extension points used to contribute to the Audit Service:
event
extendedInfo
adapter
listener
Two others extension points can be used to configure the datastorage for Audit:
queues
hibernate
‐ for the legacy SQL back-end only
Event
Those default auditable events match the Nuxeo core base events:
documentCreated
documentCreatedByCopy
documentDuplicated
documentMoved
documentRemoved
documentModified
documentLocked
documentUnlocked
documentSecurityUpdated
lifecycle_transition_event
loginSuccess
loginFailed
logout
documentCheckedIn
versionRemoved
documentProxyPublished
sectionContentPublished
documentRestored
If you are sending new Nuxeo core events and want them to be audited, you have to extend the event
extension point. Here is an example of a contribution to this extension point:
<extension target="org.nuxeo.ecm.platform.audit.service.NXAuditEventsService" point="event">
<event name="documentCreated" />
<event name="documentCreatedByCopy" />
<event name="documentDuplicated" />
<event name="documentMoved" />
<event name="documentRemoved" />
<event name="documentModified" />
<event name="documentLocked" />
<event name="documentUnlocked" />
<event name="documentSecurityUpdated" />
<event name="lifecycle_transition_event" />
<event name="loginSuccess" />
<event name="loginFailed" />
<event name="logout" />
<event name="documentCheckedIn" />
<event name="versionRemoved" />
<event name="documentProxyPublished" />
<event name="sectionContentPublished" />
<event name="documentRestored" />
</extension>
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 point="extendedInfo" target="org.nuxeo.ecm.platform.audit.service.NXAuditEventsService">
<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.ecm.platform.audit.service.NXAuditEventsService"
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
, worklowInitiator
, workflowVarriables
to the extendedInfo
only for the afterWorkflowStarted
event.
When the extension point is contributed, the data are stored into the audit.elasticsearch.indexName
index for the Elasticsearch back-end, into the NXP_LOGS_EXTINFO
and NXP_LOGS_MAPEXTINFOS
tables for the legacy SQL back-end and into the audit
collection in the audit
database for the MongoDB back-end.
Adapter
The contribution to the adapter
extension point of the org.nuxeo.ecm.platform.audit.service.NXAuditEventsService
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.ecm.platform.audit.service.NXAuditEventsService" point="adapter">
<adapter name="myadapter" class="org.nuxeo.ecm.core.api.facet.VersioningDocument"/>
</extension>
Listener
A post commit asynchronous listener is defined and an Event Bundle, which is an ordered set of events raised during a user operation, is pushed into the Audit log. Here is an example of a contribution to the listener
extension point.
<extension target="org.nuxeo.ecm.core.event.EventServiceComponent" point="listener">
<listener name="auditLoggerListener" async="true" postCommit="true"
class="org.nuxeo.ecm.platform.audit.listener.AuditEventLogger" />
</extension>
Note that since 9.3 by default this listener is overridden by the Nuxeo Stream audit writer.
Queues
It is also possible to configure queues used by the Audit Service. Each queue is using a separate queue and a single thread for logging. The extension point used to define the queues' parameters is queue
for the org.nuxeo.ecm.core.work.service
target.
<extension target="org.nuxeo.ecm.core.work.service" point="queues">
<queue id="audit">
<name>Audit queue</name>
<maxThreads>1</maxThreads>
<category>auditLoggerListener</category>
<!-- clear completed work instances older than 5 min -->
<clearCompletedAfterSeconds>300</clearCompletedAfterSeconds>
</queue>
</extension>
Hibernate - Legacy SQL Back-end Only
In the legacy SQL back-end, the Audit Service uses Hibernate as a JPA provider. The configuration is done in the hibernate
extension point for the org.nuxeo.ecm.core.persistence.PersistenceComponent
target. This extension point lets you override the default Hibernate configuration.
<extension target="org.nuxeo.ecm.core.persistence.PersistenceComponent" point="hibernate">
<hibernateConfiguration name="nxaudit-logs">
<datasource>nxaudit-logs</datasource>
<properties>
<property name="hibernate.hbm2ddl.auto">update</property>
</properties>
</hibernateConfiguration>
</extension>