Entity Log

Entity Log is a mechanism for monitoring changes made to JPA entities within a system. It records changes in entity attributes and provides a user interface for querying and displaying details regarding the changes:

  • What entity instance was changed.

  • Old and new values of changed attributes.

  • When the entity was changed.

  • Which user changed the entity.

Registering Changes

Entity Log automatically monitors changes made to JPA entities upon saving through DataManager or EntityManager. It is not operational when native SQL is utilized.

Another approach is utilizing the EntityLog bean to log changes in entities directly from your application code. In this case, invoke registerCreate(), registerModify() and registerDelete() methods with the auto parameter set to false. When the entity log is called automatically by the framework, this parameter is set to true.

Setting Up Entity Log

To be able to use the administrative UI at runtime, a user must have the entity-log resource role provided by the Audit add-on.

You can configure the entity log in the Audit → Entity Log view. Switch to the Setup tab:

entity log set

To establish entity log configuration, click the Create button.

Choose the entity from the Name drop-down list and select the attributes for auditing.

The Auto checkbox determines whether the system logs changes when EntityLog is invoked with the auto = true parameter (triggered by entity listeners).

The Manual checkbox determines whether the system logs changes when EntityLog is invoked with the auto = false parameter.

The Export and Import actions allow you to export and import configurations in JSON or ZIP format.

Also, you can set up entity log by entering some records in the database if you want to include the configuration to the database initialization.

Logging configuration involves the LoggedEntity and LoggedAttribute entities, which correspond to the AUDIT_LOGGED_ENTITY and AUDIT_LOGGED_ATTR tables.

LoggedEntity specifies the entity types to be logged.

LoggedAttribute defines the entity attribute to be logged and contains a link to the LoggedEntity and the attribute name.

To configure logging for a specific entity, insert the corresponding entries into the AUDIT_LOGGED_ENTITY and AUDIT_LOGGED_ATTR tables.

For instance, in the following example, logging changes to the phone attribute of the Customer entity is configured during database initialization:

<changeSet id="1" author="audit">
    <insert tableName="AUDIT_LOGGED_ENTITY">
        <column name="ID" value="0a6ba81c-a8b9-bc8f-3829-53a6cef48871"/>
        <column name="CREATED_BY" value="admin"/>
        <column name="CREATE_TS" valueDate="2024-02-21T14:57:25.339"/>
        <column name="NAME" value="Customer"/>
        <column name="AUTO" value="true"/>
        <column name="MANUAL" value="true"/>
    </insert>
</changeSet>
<changeSet id="2" author="audit">
    <insert tableName="AUDIT_LOGGED_ATTR">
        <column name="ID" value="8e6e9825-1381-5299-e704-eadf1b96996e"/>
        <column name="CREATE_TS" valueDate="2024-02-21T14:57:25.339"/>
        <column name="CREATED_BY" value="admin"/>
        <column name="ENTITY_ID" value="0a6ba81c-a8b9-bc8f-3829-53a6cef48871"/>
        <column name="NAME" value="phone"/>
    </insert>
</changeSet>

Viewing Entity Log

To access the content of the entity log, navigate to the View tab within the Audit → Entity Log view. Configure the required filters to locate specific log entries.

entity log view

Additionally, you can access the log entries for a particular entity from any application view.

Log entries are stored in the AUDIT_ENTITY_LOG table corresponding to the EntityLogItem entity. Changed attribute values are stored in the CHANGES column and are converted to instances of EntityLogAttr entity.

For instance, in the following example, an Order detail view displays data grids containing the entity log information.

Below is an excerpt from the view’s XML descriptor:

<data>
    <instance id="orderDc"
              class="com.company.demo.entity.Order">
        <fetchPlan extends="_base"/>
        <loader/>
    </instance>
    <collection id="entityLogItemsDc"
                class="io.jmix.audit.entity.EntityLogItem"> (1)
        <fetchPlan extends="_local"/>
        <loader id="entityLogItemsDl" readOnly="true">
            <query>
                <![CDATA[select e from audit_EntityLog e
                where e.entityRef.entityId = :entityOrder]]>
            </query>
        </loader>
        <collection id="entityLogAttrDc" property="attributes"/> (2)
    </collection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/>
</facets>
<actions>
    <action id="saveAction" type="detail_saveClose"/>
    <action id="closeAction" type="detail_close"/>
</actions>
<layout>
    <formLayout id="form" dataContainer="orderDc">
        <datePicker id="dateField" property="date"/>
        <textField id="productField" property="product"/>
        <textField id="amountField" property="amount"/>
        <textField id="priceField" property="price"/>
    </formLayout>
    <formLayout>
        <dataGrid id="entityLogItemsDataGrid"
                  dataContainer="entityLogItemsDc"> (3)
            <columns>
                <column property="eventTs"/>
                <column property="username"/>
                <column property="type"/>
            </columns>
        </dataGrid>
        <dataGrid id="entityLogAttrsDataGrid"
                  dataContainer="entityLogAttrDc"> (4)
            <columns>
                <column property="name"/>
                <column property="oldValue"/>
                <column property="value"/>
            </columns>
        </dataGrid>
    </formLayout>
    <hbox id="detailActions">
        <button id="saveAndCloseBtn" action="saveAction"/>
        <button id="closeBtn" action="closeAction"/>
    </hbox>
</layout>
1 Loading a collection of EntityLogItem into the entityLogItemsDc data container.
2 Loading associated EntityLogAttr instances into the entityLogAttrDc data container.
3 Binding a data grid to the entityLogItemsDc container.
4 Binding a data grid to the entityLogAttrDc container.

Order view controller looks like this:

@ViewComponent
private CollectionLoader<EntityLogItem> entityLogItemsDl;

@Subscribe(id = "orderDc", target = Target.DATA_CONTAINER)
public void onOrderDcItemChange(final InstanceContainer.ItemChangeEvent<Order> event) { (1)
    entityLogItemsDl.setParameter("entityOrder",event.getItem().getId());
    entityLogItemsDl.load();
}
1 In the ItemChangeEvent handler of the orderDc container, a parameter is set to the dependent loader and it is triggered.