Lesson 10: Audit Trail

Overview

The Audit Trail is a GeoCalc feature that keeps track of changes made to the objects in the DataSource. This feature can be used as quality management tool for geodesists, as well as a simple change track log for most users. The Audit trail can also provide insight when transformation results differ with previously converted data, as it may be the case that a change in an object's definition is responsible for the difference in results.

Setup

In order to make use of the Audit Trail, you must first create and load a DataSource. To do this, we will call one of the LoadFile methods on the Datasource object that allows us to specify change log files. As a measure of best practice, log files generally have the extension .xch. When a new Datasource is created and loaded, the change file will not exist. It will only be created by GeoCalc as changes are made and committed to the reference XML file. If the path specified during the LoadFile call already exists, the change log attached to the current instance will be loaded. If the files do not yet exist, then the paths will dictate the name and location of the log files that will be created once an object has been added or modified. Here is how we might create and load the DataSource:

DataSource* datasource = DataSource::CreateDataSource();

datasource->LoadFile(BMG_T("c:/bmg/GeoCalcPBW/data/geodata.xml"), true,

BMG_T("c:/bmg/GeoCalcPBW/data/custom.xml"),

BMG_T("c:/bmg/GeoCalcPBW/data/geodata.xch"),

BMG_T("c:/bmg/GeoCalcPBW/data/custom.xch"));

This call will load geodata.xml as the primary datasource file, and custom.xml as the custom definition file. The geodata.xch file will be used to store the change log information for geodata.xml, and custom.xch will store the change log information for custom.xml. The second parameter, true, indicates that the base datasource, geodata.xml, will be loaded as read-only, and none of the base objects contained in the file can be modified. This is a good practice, which ensures that the basic geodetic definitions that ship with GeoCalc do not get changed by the not so careful end user.  It will still be possible to add new objects to the DataSource and modify these objects, but they will be stored in the custom.xml datasource file. Additionally, you can attach a user identification string to a given Audit Trail file using the SetCurrentUserID method on the DataSource. This UserID will be added to each change in the audit log.

Assuming that the geodata.xch and custom.xch files did not exist when the DataSource was loaded, GeoCalc will now have an empty Audit Trail. If these files did exist, then the Audit Trail will be populated with the logs from these files.

Making Changes

Let's test out our new Audit Trail by making a simple change to the DataSource. We will add a new AngularUnit, since that is one of the easiest objects to construct:

AngularUnit* au = AngularUnit::CreateAngularUnit();

au->set_Name(L"MyUnit");

au->set_Abbreviation(L"MyU");

au->set_UnitsPerDegree(1.12345);

au->get_Identifiers().Add(L"GC", L"MyUnit");

datasource->PutAngularUnit(*au);

Disposal::Dispose(au);

Since our base datasource is locked, this change will go into the custom datasource.

Reviewing Changes

In order to review the Audit Trail, we will have to retrieve a ChangeLog object from the DataSource.  Since we just modified our custom datasource, we will use the DataSource::get_CustomChangeLog function, like so:

const ChangeLog* customLog = datasource->get_CustomChangeLog();

Since the change we made to the DataSource has not yet been saved to file, we can access the changes using either the ChangeLog::get_AllChanges or ChangeLog::get_UncommittedChanges functions.  Once we get the changes, we will iterate through them and print out a description of the changes:

const ChangeLog* customLog = dataSource->get_CustomChangeLog();

std::list<const Change*> changes = std::list<const Change*>();

customLog->get_UncommittedChanges(changes);

 

for each(const Change * change in changes)

{

CString changeStr = CString("Object \"") + CString(change->get_ObjectName()) + CString("\"");

 

if(eChangeAdd == change->get_ChangeType())

{

changeStr += CString(" added");

}

else if(eChangeRemove == change->get_ChangeType())

{

changeStr += CString(" removed");

}

else if(eChangeModify == change->get_ChangeType())

{

changeStr += CString(" modified");

}

 

changeStr += CString(" at ") + CString(change->get_Timestamp());

changeStr += CString(" Made by User: ") + CString(change->get_UserID());

AfxMessageBox(changeStr);

}

It is the caller's responsibility to free all Change objects that are provided by GeoCalc. This next line will ensure that all such objects are released:

Disposal::DisposeVector(changes);

It is possible to get much more information about the changes that have been made by getting the ChangeDetail objects with the Change::get_ChangeDetail method. This method returns a vector of ChangeDetail objects, since a single Change may encapsulate several smaller changes.  As with the vector of Change objects it is the caller's responsibility to free all ChangeDetail objects that are provided by GeoCalc.

Saving

The change log files will be saved whenever the CommitToFile method is called.  If the log files do not exist at this time, and there are changes that have been made to the DataSource, the log files will be created. The base log file will only be created or updated if there was a change to the base DataSource since the last load or save operation, and the custom log file will only be created or updated if there was a change to the custom datasource since the last DataSource load or save. Here is an example of how the DataSource and log files may be saved:

datasource->CommitToFile();

 

Cleanup

It is now necessary to clean up after ourselves and ensure that we don't have any memory leaks, which we can do like so

if(datasource) Disposal::Dispose(datasource);