Object Auditing
Enabling object auditing
To enable object auditing, add the -Dep.audit.enabled=true
JVM property to your Tomcat startup scripts.
To control which domain objects are audited, in the ep-core project, open WEB-INF/conf/spring/dataaccess/openjpa/openjpa.xml
and locate the auditEntityListener
bean definition. The auditableClasses
property specifies the classes that will be audited. If you want to audit other domain objects, you must add their class names to this list.
<bean id="auditEntityListener" class="com.elasticpath.persistence.impl.AuditEntityListener">
<property name="beanFactory" ref="coreBeanFactory"/>
<property name="auditableClasses">
<set>
<value>com.elasticpath.domain.attribute.impl.AbstractAttributeValueImpl</value>
<value>com.elasticpath.domain.attribute.impl.ProductAttributeValueImpl</value>
<value>com.elasticpath.domain.attribute.impl.SkuAttributeValueImpl</value>
<value>com.elasticpath.domain.catalog.impl.DigitalAssetImpl</value>
</set>
</property>
<property name="nonAuditableNamedQueries">
<set>
<!-- <value></value> -->
</set>
</property>
<property name="metadataMap" ref="persistenceListenerMetadataMap"/>
</bean>
After making the changes, you must rebuild and redeploy the Elastic Path Commerce services.
When auditing is enabled, information related to the changed objects is written to the audit tables in the database.
Note that you can include custom metadata in the audit records. For more information, see Including audit metadata.
Including audit metadata
When Object Auditing is enabled, Commerce Manager passes transaction-related metadata to the persistence layer, such as the Commerce Manager user ID, so that it can be included in the audit records.
To include additional audit metadata, create a method similar to the following and pass in the metadata you wish to store in the persistence layer. The following example does so for the userGuid.
private void storeUserIdForAuditing(final CmUser cmUser) {
Map<String, String> metadata = ServiceLocator.getService(ContextIdNames.PERSISTENCELISTENER_METADATA_MAP);
metadata.put(WebConstants.USER_GUID, cmUser.getGuid());
}
Your new userGuid metadata is then passed to the following map obtained from the Spring Context:
<bean id="persistenceListenerMetadataMap" class="com.elasticpath.commons.ThreadLocalMap" scope="singleton" />
Next, override AuditDaoImpl.processMetadata()
to handle the extended metadata. The code after the call to super.processMetadata()
may be different based on the extended metadata you are handling:
public class ExtAuditDaoImpl extends AudiDaoImpl {
public void processMetadata(ChangeTransaction csTransaction, Object persistable, Map<String, Object> metadata) {
super.processMetadata(csTransaction, persistable, metadata);
String customAttr= (String)metadata.get("customAttr");
if(customAttr!= null) {
ChangeTransactionMetadata customMetadata = this.generateMetadata(csTransaction, "customAttr", customAttr);
this.getPersistenceEngine().save(customMetadata );
}
}
}
The ThreadLocalMap
class is an implementation of the Map
interface that is local to the current thread. It will contain key/value pairs of audit metadata that can be consumed by the auditing listener and stored as part of the audit records.
Object auditing database schema
The following database tables are used by object auditing:
TCHANGETRANSACTION
: Contains transactions (a transaction surrounds a collection of change operations)TCHANGETRANSACTIONMETADATA
: Contains metadata associated with transactions (for example, references to the user and the Change Sets)TCHANGEOPERATION
: Contains change operationsTDATACHANGED
: For each changed object, this table contains:- The UID or GUID of the object that was modified
- Object type
- The modified field name
- The original value
- The new value
- The type of change
For details on the data stored in these tables, see Understanding audit data.
note
Auditing data is only tracked if Enabling object auditing.
Understanding audit data
When Object auditing is enabled for a particular type of object, any changes to objects of that type generate audit data in a few different tables.
TCHANGEOPERATION
A row is added to the TCHANGEOPERATION
table each time a change occurs. TCHANGEOPERATION
contains the following information:
QUERY_PARAMETERS
The JPQL (Java Persistence Query Language) query parameters (in the case of a delete).
QUERY_STRING
The JPQL query that persisted the change (in the case of a delete).
ROOT_OBJECT_GUID
The GUID of the top-level object that was modified.
ROOT_OBJECT_NAME
The name (usually the fully-qualified class name) of the top-level object that was modified.
TYPE
Whether the change was part of a bulk operation or a single change.
CHANGE_TRANSACTION_UID
Foreign key on
TCHANGETRANSACTION
TCHANGETRANSACTION
A row is added to the TCHANGETRANSACTION
table each time a change occurs. It contains a timestamp indicating the date and time the change occurred.
TCHANGETRANSACTIONMETADATA
One or more rows are added to the TCHANGETRANSACTIONMETADATA
table each time a change occurs. One row is added containing the GUID of the Change Sets that contains the modified object.
TDATACHANGED
For auditing purposes, the most useful information from a business perspective is in the TDATACHANGED
table.
Usually at least two rows are added each time a change occurs. Each row contains details about specific data that was changed during the operation. TDATACHANGED
contains the following information:
CHANGE_TYPE
The type of change (update, delete, etc.).
FIELD_NAME
The name of the field on the class containing the changed value.
FIELD_NEW_VALUE
The value of the field after the change.
FIELD_OLD_VALUE
The value of the field before the change.
OBJECT_GUID
The GUID of the object that was modified. If the object does not have a GUID, this column contains
NULL
.OBJECT_NAME
The name (usually the fully-qualified class name) of the top-level object that was modified
For example, if a CM user modifies the brand and the display name of a product object, the following information would be stored in TDATACHANGED
.
In this case, the top-level object that was changed is a ProductImpl
, but the displayName
field is stored in a sub-object (LocaleDependantFieldsImpl
).