Customize the Audit Trail System

The Audit Trail System represented by the Audit Trail module provides a ready-to-use mechanism for logging changes made to persistent objects. The default audit settings are described below.

  • All persistent classes (inherited from the Base Persistent Classes) are audited by the Audit Trail system.
  • All public simple and reference properties owned by persistent classes are audited. However, read-only properties (properties with no set accessor) are not audited.
  • All public collection properties owned by persistent classes are audited.
  • All object changes - object creation, deletion and modification, names of modified properties, initial and actual values of properties are fully audited. Current user name provided by the Security System is also saved.
  • Audit information is logged to the application database.
  • The real values of properties stored as blob data (e.g. images) are not saved. The "Blob data" string is saved instead.
  • The null values are saved as the "N/A" string.
Note

When the object is modified in the Detail View, the initial values are copied immediately when an object has been loaded. When the object is modified in Editable List View, initial values are copied in the BaseObjectSpace.ObjectChanged event. The actual values are collected and saved in a separate Unit of Work in the BaseObjectSpace.Committed event.

You can customize Audit Trail System at different points. This topic details various customizations.

Change Object Auditing Mode

The Audit Trail System supports several object auditing modes.

Mode Description
Full All object changes are fully audited. This mode is the default.
Lightweight Only object creation, deletion and modification are audited. This mode does not audit names and values of changed properties.
CreationOnly Only object creation is audited. Intended for data import purposes.

To change the current object auditing mode, set the AuditTrailService.Instance.ObjectAuditingMode property to one of the ObjectAuditingMode enumeration values. The following code demonstrates how this can be implemented.

XAF Windows Forms Application - Program.cs (Program.vb) file:

static void Main() {
    MySolutionWindowsFormsApplication application = new MySolutionWindowsFormsApplication();
    // ...
    AuditTrailService.Instance.ObjectAuditingMode = ObjectAuditingMode.Lightweight;
    application.Setup();
    application.Start();
    //...
}

XAF ASP.NET Application - Global.asax.cs (Global.asax.vb) file:

protected void Session_Start(object sender, EventArgs e) {
    WebApplication.SetInstance(Session, new MySolutionWebApplication());
    AuditTrailService.Instance.ObjectAuditingMode = ObjectAuditingMode.Lightweight;
    //...
    WebApplication.Instance.Setup();
    WebApplication.Instance.Start();
}

Audit the Current User or Host Identity

If you build a business application that is intended for use by several end-users at the same time, you need to get information on who made a particular change. For this purpose, the CurrentUserName property of the application's security system is passed to the Audit Trail system. If you do not use the XAF security system, in an XAF Windows Forms application you can get the user name from the WindowsIdentity object. In an XAF ASP.NET Web application, you can use the value returned by the HttpContext.Current.Request.UserHostAddress property. The following code demonstrates how this can be implemented.

Note

If you do not specify the current user for the Audit Trail system, the null value will be logged.

XAF Windows Forms Application - Program.cs (Program.vb) file:

using System.Security.Principal;
//...
static void Main() {
    MySolutionWindowsFormsApplication application = new MySolutionWindowsFormsApplication();
    // ...
    //Subscribe to QueryCurrentUserName event of the AuditTrailService's instance
    AuditTrailService.Instance.QueryCurrentUserName += 
       new QueryCurrentUserNameEventHandler(Instance_QueryCurrentUserName);
    application.Setup();
    application.Start();
    //...
}
static void Instance_QueryCurrentUserName(object sender, QueryCurrentUserNameEventArgs e) {
    e.CurrentUserName = WindowsIdentity.GetCurrent().Name;
}

XAF ASP.NET Application - Global.asax.cs (Global.asax.vb) file:

protected void Session_Start(object sender, EventArgs e) {
    WebApplication.SetInstance(Session, new MySolutionWebApplication());
    AuditTrailService.Instance.QueryCurrentUserName += 
        new QueryCurrentUserNameEventHandler(Instance_QueryCurrentUserName);
    //...
    WebApplication.Instance.Setup();
    WebApplication.Instance.Start();
}
private void Instance_QueryCurrentUserName(object sender, QueryCurrentUserNameEventArgs e) {
    e.CurrentUserName = 
        String.Format("Web user ({0})", HttpContext.Current.Request.UserHostAddress);
}

Specify the Objects and Properties to Be Audited

You can modify default audit settings, and specify objects and properties to be audited or excluded from the audit process. For this purpose, subscribe to the CustomizeAuditTrailSettings event of a AuditTrailService.Instance object. The following code demonstrates how this can be implemented.

XAF Windows Forms Application - Program.cs (Program.vb) file:

using DevExpress.Persistent.AuditTrail;
//...
static void Main() {
    MySolutionWindowsFormsApplication application = new MySolutionWindowsFormsApplication();
    // ...
    AuditTrailService.Instance.CustomizeAuditTrailSettings += 
        new CustomizeAuditSettingsEventHandler(Instance_CustomizeAuditTrailSettings);
    application.Setup();
    application.Start();
    //...
}
static void Instance_CustomizeAuditTrailSettings(object sender, 
    CustomizeAuditTrailSettingsEventArgs e) {
    e.AuditTrailSettings.Clear();
    e.AuditTrailSettings.AddType(typeof(MyObjectType), "FirstPropertyToBeAudited", "SecondPropertyToBeAudited");
}

XAF ASP.NET Application - Global.asax.cs (Global.asax.vb) file:

using DevExpress.Persistent.AuditTrail;
//...
protected void Session_Start(object sender, EventArgs e) {
    WebApplication.SetInstance(Session, new MySolutionWebApplication());
    AuditTrailService.Instance.CustomizeAuditTrailSettings += 
        new CustomizeAuditSettingsEventHandler(Instance_CustomizeAuditTrailSettings);
    //...
    WebApplication.Instance.Setup();
    WebApplication.Instance.Start();
}
void Instance_CustomizeAuditTrailSettings(object sender, 
    CustomizeAuditTrailSettingsEventArgs e) {
    e.AuditTrailSettings.Clear();
    e.AuditTrailSettings.AddType(typeof(MyObjectType), "FirstPropertyToBeAudited", "SecondPropertyToBeAudited");
}

Take note of the CustomizeAuditTrailSettings event handler. The default settings are removed by the AuditTrailSettings.Clear method. The properties to audit are added by the AuditTrailSettings.AddType method. You can use another overload of this method, which does not require specifying members names.

e.AuditTrailSettings.AddType(typeof(MyObjectType));

All members owned by the class, which are public and not read-only, will be audited. If MyObjectType exposes a read-only property to be audited, add it manually.

e.AuditTrailSettings.AddType(typeof(MyObjectType), "ReadOnlyPropertyToBeAudited");

The AuditTrailSettings.RemoveProperties method is used to exclude properties from the audit process.

e.AuditTrailSettings.RemoveProperties(typeof(MyObjectType), "PropertyToBeExcluded");

If you use Domain Components (DC), do not pass the DC interface type to the AddType method. Instead, pass the generated entity type.

e.AuditTrailSettings.AddType(
    XpoTypesInfoHelper.GetXpoTypeInfoSource().GetGeneratedEntityType(typeof(IOrder)));

You can use the CustomizeAuditTrailSettings event to customize the AuditTrailSettings object in other ways. For example, you can specify properties that are not to be audited. For this purpose, use the AuditTrailSettings.RemoveProperties method.

Customize the Data Storing Mechanism

By default, audit information is logged to the application database (see Audit Trail Module Overview). You can customize this process by handling the AuditTrailService.Instance.SaveAuditTrailData event, which is raised when an object is committed to the database. In this event handler, you have to replace the default saving mechanism of the AuditDataItem objects with a custom one. To disable the default storing procedure, you should set the handler's SaveAuditTrailDataEventArgs.Handled parameter to true. The following code demonstrates how this can be implemented.

XAF Windows Forms Application - Program.cs (Program.vb) file:

using DevExpress.Persistent.AuditTrail;
//...
static void Main() {
    MySolutionWindowsFormsApplication application = new MySolutionWindowsFormsApplication();
    // ...
    AuditTrailService.Instance.SaveAuditTrailData += 
        new SaveAuditTrailDataEventHandler(Instance_SaveAuditTrailData);
    application.Setup();
    application.Start();
    //...
}
static void Instance_SaveAuditTrailData(object sender, SaveAuditTrailDataEventArgs e) {
    //Save the data passed as the e.AuditTrailDataItems parameter
    //Disable the default storing procedure if it is necessary
    e.Handled = true;
}

XAF ASP.NET Application - Global.asax.cs (Global.asax.vb) file.

using DevExpress.Persistent.AuditTrail;
//...
protected void Session_Start(object sender, EventArgs e) {
    WebApplication.SetInstance(Session, new MySolutionWebApplication());
    AuditTrailService.Instance.SaveAuditTrailData += 
        new SaveAuditTrailDataEventHandler(Instance_SaveAuditTrailData);
    //...
    WebApplication.Instance.Setup();
    WebApplication.Instance.Start();
}
static void Instance_SaveAuditTrailData(object sender, SaveAuditTrailDataEventArgs e) {
    //Save the data passed as the e.AuditTrailDataItems parameter
    //Disable the default storing procedure if it is necessary
    e.Handled = true;
}

Another way to customize the mechanism for storing audit information is to inherit from the AuditDataClass, and override the Save method. In this instance, you have to initialize the AuditTrailService.Instance.AuditDataStore property with an instance of your class.

Implement Custom Persistent Object to be Used as the Audit Data Store

By default, the AuditDataItemPersistent class is used as the audit data store. If you want to store extra audit information, inherit from this class, or implement the IAuditDataItemPersistent interface "from scratch". In the overridden OnSaving method of your AuditDataItemPersistent descendant, you can initialize extra properties based on the AuditedObject, PropertyName and other info from the base class. To use your custom class instead of the default one, set the AuditDataItemPersistentType property of the Audit Trail module in the Application Designer (not in the Module Designer).

Specify the String Representation of the Null Value

By default, the null value is saved as the "N/A" string. If you desire, you can change its string representation. For this purpose, use the AuditTrailService.Instance.AuditDataStore.NullValueString property.

Specify the String Representation of Reference Type Properties

Implement a descendant of the AuditDataStore class and override the GetDefaultStringRepresentation method. In addition, set the AuditTrailService.Instance.AuditDataStore property to an instance of your class.

Customize the Blob Properties Storage Mechanism

By default, Blob property values are not saved. The "Blob data" string is saved instead. You can customize this behavior by implementing the AuditDataStore class and overriding the Save method. In this instance, set the AuditTrailService.Instance.AuditDataStore property to an instance of your class.

Add Custom Data to the Audit Log

Object changes are represented by AuditDataItem objects (see Audit Trail Module Overview). You can add custom data (information on events that are not audited) to the audit log directly. This means that you can add an AuditDataItem object to the Audit Data Items collection. For this purpose, specify the following code.

AuditDataItem customDataItem = new AuditDataItem(myAuditedObject, null, 
    "CustomOldValue", "CustomNewValue", AuditOperationType.CustomData);
AuditTrailService.Instance.AddCustomAuditData(mySession, customDataItem);
AuditTrailService.Instance.SaveAuditData(mySession);

This code can be implemented at any point in a program. For instance, in a custom Controller.

Note

You should always invoke the AuditTrailService.Instance.SaveAuditData method after adding custom audit data manually, without reusing the Session of the View's ObjectSpace. Otherwise, the audit data will not be written to the database.

See Also