Working with Auditing-related Objects

This topic provides code examples for the following auditing operations:

Note that you can audit a custom event by raising it on an object. Auditing a custom event is, in large part, a matter of creating the custom event and raising it on an object that is defined as the target of a subscription. For an example of this, see Creating and Raising a Custom Event. In order for a custom event to be audited when raised, you must first configure it for auditing, as described in Configuring a Class for Auditing.

For an overview of auditing functionality, see Auditing Concepts.

Configuring a Class for Auditing

Auditing is configured on a per-class basis, in which a class is represented by a SubscribableClassDefinition object. An event to be audited for a class is represented by an EventClassDefinition object. The audit configuration information is held in an AuditDefinition object, and contains an EventClassDefinition object. There is one AuditDefinition object for each event to be audited in a class. A SubscribableClassDefinition object can contain one or more AuditDefinition objects.

The following steps describe how to configure a class for auditing:

  1. Get a SubscribableClassDefinition object or subobject, such as the Document class.
  2. Get an EventClassDefinition object, such as the CreationEvent.
  3. Create an AuditDefinition object.
  4. Create an AuditDefinitionList object.
  5. Set the AuditDefinition object properties, such as EventClass, which takes the EventClassDefinition object.
  6. Add the AuditDefinition object to the AuditDefinitionList object.
  7. Repeat steps 2 through 6 for each event to be audited for a class.
  8. Set the SubscribableClassDefinition object's AuditDefinitions property to the AuditDefinitionList object.

In the following Java™ and C# examples, two events associated with the Document class are configured for auditing. The Document class is represented by DocumentClassDefinition, a subclass of SubscribableClassDefinition. The configured events are a system event, CreationEvent, and a custom event. The custom event was previously created and is specified by a GUID. The events to be audited are set in the EventClass property. Note that in addition to the EventClass property, other AuditDefinition object properties are set. Also note that the code verifies that auditing is enabled on the object store before the class is configured for auditing.

Java Example

// Make sure auditing is enabled for the object store
if (os.get_AuditLevel()==AuditLevel.NONE)
{
   os.set_AuditLevel(AuditLevel.ENABLED);
   os.save(RefreshMode.REFRESH);
}

// Fetch class definition for Document
DocumentClassDefinition dcd = Factory.DocumentClassDefinition.fetchInstance(os, GuidConstants.Class_Document, null);

// Get AuditDefinitionList from DocumentClassDefinition object
AuditDefinitionList adl = dcd.get_AuditDefinitions();

// Create AuditDefinition object to be set with a system event
AuditDefinition ad = Factory.AuditDefinition.createInstance(os);
// Get EventClassDefinition for system event
EventClassDefinition ecd = Factory.EventClassDefinition.getInstance(os, GuidConstants.Class_CreationEvent);
// Set properties on AuditDefinition object for system event
ad.set_EventClass(ecd);
ad.set_AuditSuccess(new Boolean("TRUE"));
ad.set_AuditFailure(new Boolean("TRUE"));
ad.set_IncludeSubclassesRequested(new Boolean("TRUE"));
ad.set_ObjectStateRecordingLevel(ObjectStateRecordingLevel.NONE);

// Add first audit definition to list for system event
adl.add(ad);

// Create AuditDefinition object to be set with a custom event
AuditDefinition ad2 = Factory.AuditDefinition.createInstance(os);
// Get EventClassDefinition for custom event
EventClassDefinition ecd2 = Factory.EventClassDefinition.getInstance(os, new Id("{9906D34A-DBE2-415E-B807-6B4CCA65B893}"));
// Set properties on AuditDefinition object for custom event
ad2.set_EventClass(ecd2);
ad2.set_AuditSuccess(new Boolean("TRUE"));
ad2.set_AuditFailure(new Boolean("TRUE"));
ad2.set_IncludeSubclassesRequested(new Boolean("TRUE"));
ad2.set_ObjectStateRecordingLevel(ObjectStateRecordingLevel.ORIGINAL_AND_MODIFIED_OBJECTS);
// Add second audit definition to list for custom event
adl.add(ad2);

// Set AuditDefinitions property on DocumentClassDefinition object
dcd.set_AuditDefinitions(adl);

// Save DocumentClassDefinition object
dcd.save(RefreshMode.REFRESH);

C# Example

// Make sure auditing is enabled for the object store
if (os.AuditLevel == AuditLevel.NONE)
{
   os.AuditLevel = AuditLevel.ENABLED;
   os.Save(RefreshMode.REFRESH);
}

// Fetch class definition for document
IDocumentClassDefinition dcd = Factory.DocumentClassDefinition.FetchInstance(os, GuidConstants.Class_Document,null);

// Get AuditDefinitionList from DocumentClassDefinition object
IAuditDefinitionList adl = dcd.AuditDefinitions;

// Create audit definition to be set with a system event
IAuditDefinition ad = Factory.AuditDefinition.CreateInstance(os);

// Get EventClassDefinition for system event
IEventClassDefinition ecd = Factory.EventClassDefinition.GetInstance (
   os, GuidConstants.Class_CreationEvent);

// Set properties on audit definition object for system event
ad.EventClass = ecd;
ad.AuditSuccess = true;
ad.AuditFailure = true;
ad.IncludeSubclassesRequested = true;
ad.ObjectStateRecordingLevel=ObjectStateRecordingLevel.NONE;

// Add first audit definition to list for system event
adl.Add(ad);

// Create AuditDefinition object to be set with a custom event
IAuditDefinition ad2 = Factory.AuditDefinition.CreateInstance(os);

// Get EventClassDefinition for custom event
IEventClassDefinition ecd2 = Factory.EventClassDefinition.GetInstance (
   os, new Id("{9906D34A-DBE2-415E-B807-6B4CCA65B893}"));

// Set properties on AuditDefinition object for custom event
ad2.EventClass = ecd;
ad2.AuditSuccess = true;
ad2.AuditFailure = true;
ad2.IncludeSubclassesRequested = true;
ad2.ObjectStateRecordingLevel=ObjectStateRecordingLevel.ORIGINAL_AND_MODIFIED_OBJECTS;

// Add second audit definition to list for custom event
adl.Add(ad2);

// Set AuditDefinitions property on DocumentClassDefinition object
dcd.AuditDefinitions = adl;

// Save DocumentClassDefinition object
dcd.Save(RefreshMode.REFRESH);

Retrieving Audit Definitions for a Class

You can retrieve the audit definitions defined for a class by getting the AuditDefinitions property on a SubscribableClassDefinition-derived object, which returns a list of AuditDefinition objects. By iterating the list for AuditDefinition objects, you can find the audited events for which the class is configured.

One use case for retrieving audit definitions is to verify that a particular audit event is configured for a class. In the following Java and C# code snippets, the Document class, as represented by a DocumentClassDefinition object, is tested for a custom event, which is specified by a GUID. If the Document class is configured to be audited for the custom event, the code prints out configuration information about the audited custom event.

Java Example

// Retrieve class definition for Document
DocumentClassDefinition dcd = Factory.DocumentClassDefinition.fetchInstance (
   os, GuidConstants.Class_Document, null);

// Get list of AuditDefinition objects configured for the Document class,
// iterate through it, and test each AuditDefinition object for
// the custom event.
AuditDefinitionList adl=dcd.get_AuditDefinitions();
Iterator adlIter = adl.iterator();
while (adlIter.hasNext())
{
   AuditDefinition ad = (AuditDefinition) adlIter.next();
   EventClassDefinition ecd = ad.get_EventClass();
   if (ecd.get_Id().equals(new Id("{9906D34A-DBE2-415E-B807-6B4CCA65B893}")))
   {
         System.out.println (
            "Audited class is " + dcd.get_DisplayName() + "\n" +
            "Audited custom event is " + ecd.get_DisplayName() + "\n" +
            "Audit successes? " + ad.get_AuditSuccess() + "\n" +
            "Audit failures? " + ad.get_AuditFailure() + "\n" +
            "Audit subclasses? " + ad.get_IncludeSubclassesRequested() + "\n" +
            "Recording level? " + ad.get_ObjectStateRecordingLevel()
         );
   }
}

C# Example

// Retrieve class definition for Document
IDocumentClassDefinition dcd = Factory.DocumentClassDefinition.FetchInstance (
   os, GuidConstants.Class_Document, null);

// Get list of AuditDefinition objects configured for the Document class,
// iterate through it, and test each AuditDefinition object for the custom event.
IAuditDefinitionList adl = dcd.AuditDefinitions;
foreach (IAuditDefinition ad in adl)
{
   IEventClassDefinition ecd = ad.EventClass;
   if (ecd.Id.Equals(new Id("{9906D34A-DBE2-415E-B807-6B4CCA65B893}")))
   {
      System.Console.WriteLine
      (
         "Audited class is " + dcd.Name + "\n" +
         "Audited custom event is " + ecd.DisplayName + "\n" +
         "Audited successes? " + ad.AuditSuccess + "\n" + 
         "Audited failures? " + ad.AuditFailure + "\n" +
         "Audit subsclasses? " + ad.IncludeSubclassesRequested + "\n" +
         "Recording level? " + ad.ObjectStateRecordingLevel
      );
   }
}

Retrieving Audit History for an Object

You can retrieve audit history for instances of classes with the AuditedEvents property. (You can also query the audit event log for historical information.) The following Java and C# code snippets retrieve a Document object's AuditedEvents property, and then they print property values for each Event object in the EventSet collection.

Java Example

// Get Document object from which audit history will be retrieved
Document doc=Factory.Document.fetchInstance(os, new Id("{7AFAC25E-D27B-49A8-B31C-E555EC87608A}"),null);

// Get audited events that have been fired on Document object, if any
EventSet es = doc.get_AuditedEvents();

// If EventSet collection not empty, iterate it for events that
// have been fired on Document object.
if (!es.isEmpty())
{
   Iterator esIter = es.iterator();
   while (esIter.hasNext())
   {
      Event event = (Event) esIter.next();

      System.out.println(
         "Event is " + event.getClassName() + "\n" +
         "Event ID is " + event.get_Id().toString()+ "\n" +
         "Event initiator is " + event.get_InitiatingUser() + "\n" +
         "Event date is " + event.get_DateCreated().toString()
      );
   }
}
else System.out.println("No audited events have been fired for this object");

C# Example

// Get Document object from which audit history will be retrieved
IDocument doc = Factory.Document.FetchInstance (
   os, new Id("{7AFAC25E-D27B-49A8-B31C-E555EC87608A}"), null);

//Get audited events that have been fired on Document object, if any 
IEventSet es = doc.AuditedEvents;

// If EventSet collection is not empty, iterate it for events that have been fired on Document object
if (!es.IsEmpty())
{
   foreach (IEvent docEvent in es)
   {
      System.Console.WriteLine
      (
         "Event is " + docEvent.GetClassName() + "\n" +
         "Event ID is " + docEvent.Id.ToString() + "\n" + 
         "Event initiator is " + docEvent.InitiatingUser + "\n" + 
         "Event date is " + docEvent.DateCreated.ToString() 
      );
   }
}
else
{
   System.Console.WriteLine("No audited events have been fired for this object");
}

Querying the Audit Event Log

You can query the audit event log, which is the Event table in an object store's database. The easiest way to query the log is with Enterprise Manager, from which you can create SQL searches or launch out-of-the-box search templates (see Concepts: search and bulk operations). However, as described in this section, you can also use the Content Java and .NET APIs to work with the audit log.

The APIs include query classes designed for ad hoc searches, allowing you to pass a SQL statement specifying the Event object to be searched and the object's properties to be retrieved. As shown in the Java and C# examples below, a SearchSQL object is constructed with a SQL statement string that specifies a search for creation events. To execute the search, the SearchSQL object is passed in the fetchRows call on the SearchScope object. Then the property values are printed out for each recorded creation event.

For overview information, see Auditing and Trace Logging and Search Concepts. For information on which properties are searchable, selectable, and orderable, refer to the metadata for each property in Event Properties.

Java Example

// Build the SQL select statement
String sqlString = "SELECT " + PropertyNames.ID +"," + PropertyNames.DATE_CREATED + "," + 
   PropertyNames.INITIATING_USER + "," + PropertyNames.EVENT_STATUS;

// Construct SearchSQL object to be used in the search operation for creation events. See Note below.
SearchSQL sql = new SearchSQL(sqlString + " FROM " + GuidConstants.Class_CreationEvent +
   " ORDER BY " + PropertyNames.DATE_CREATED + " DESC");

// Execute the search operation
SearchScope ss = new SearchScope(os);
RepositoryRowSet rrc = ss.fetchRows(sql, null, null, null);

// Iterate and print search results
Iterator it = rrc.iterator();
while (it.hasNext())
{
   RepositoryRow rr = (RepositoryRow) it.next();
   System.out.println
   (
      rr.getProperties().getIdValue(PropertyNames.ID).toString()+ "\n" +
      rr.getProperties().getDateTimeValue(PropertyNames.DATE_CREATED)+ "\n" +
      rr.getProperties().getStringValue(PropertyNames.INITIATING_USER) + "\n" +
      rr.getProperties().getInteger32Value(PropertyNames.EVENT_STATUS)
   );
}

C# Example

// Build the SQL select statement
string sqlString = "SELECT " + PropertyNames.ID + "," + PropertyNames.DATE_CREATED + "," +
   PropertyNames.INITIATING_USER + "," + PropertyNames.EVENT_STATUS;

// Construct SearchSQL object to be used in the search operation. See Note below.
SearchSQL sql = new SearchSQL(sqlString + " FROM " + GuidConstants.Class_CreationEvent +
   " ORDER BY " + PropertyNames.DATE_CREATED + " DESC");

// Execute the search operation
SearchScope ss = new SearchScope(os);
IRepositoryRowSet rrc = ss.FetchRows(sql, null, null, null);

// Iterate and print search results
foreach (IRepositoryRow rr in rrc)
{
   System.Console.WriteLine
   (
      rr.Properties.GetIdValue(PropertyNames.ID).ToString() + "\n" +
      rr.Properties.GetDateTimeValue(PropertyNames.DATE_CREATED)+ "\n" +
      rr.Properties.GetStringValue(PropertyNames.INITIATING_USER) + "\n" +
      rr.Properties.GetInteger32Value(PropertyNames.EVENT_STATUS) + "\n" 
   );
}

NOTE The above examples show how to retrieve from the log auditing information for a specified event (creation). Another use case is to get auditing information for a specified object. For example, you could construct a query for all Event objects where the SourceObjectId property is equal to an auditable object of interest.

Retrieving Source Objects

As described in Source Object Persistence, you can configure an AuditDefinition to record the source object of an event (the object on which the event is fired). Then, when the event is triggered, the source object is persisted to the audit event log (the Event table in the database), along with other event information.

Because of the extra database space required to store source objects in audited events, you can configure the recording level of the source object with the AuditDefinition.ObjectStateRecordingLevel property. The recording levels are ORIGINAL_AND_MODIFIED_OBJECTS (both the original, pre-event object and the modified, post-event object are recorded), MODIFIED_OBJECT (only the modified, post-event object is recorded), and NONE (no source objects are recorded). If NONE is specified, then the event's ModifiedProperties property will also be empty because this value is derived from the event's SourceObject property.

The following Java and C# examples show how to retrieve source objects from the audit event log. The SQL statement specifies that checkin events of a certain age be retrieved from the log, and includes properties for the source modified object and the source original object. After the search executes, the code iterates the results. For each audited Event object, the code prints information about the event and the event's source objects, if any. The code checks for events in which only the modified source object was recorded or in which no source objects were recorded.

NOTE Because there can be substantial numbers of records in the Event table, it is advised that an SQL statement that queries this table include a WHERE clause to limit the result set.

Java Example

// Build the SQL select statement
String sqlString = "SELECT " + PropertyNames.ID +"," + PropertyNames.DATE_CREATED + "," + PropertyNames.EVENT_STATUS + 
        "," + PropertyNames.ORIGINAL_OBJECT + "," + PropertyNames.SOURCE_OBJECT;

// Construct SearchSQL object to be used in search operation
SearchSQL sql = new SearchSQL(sqlString + " FROM " + GuidConstants.Class_CheckinEvent + 
        " WHERE " + PropertyNames.DATE_CREATED + "> 20080401T080000Z" +
        " ORDER BY " + PropertyNames.DATE_CREATED);

// Execute the search operation
SearchScope ss = new SearchScope(os);
RepositoryRowSet rrc = ss.fetchRows(sql, null, null, null);

// Iterate and print results
Iterator it = rrc.iterator();
while (it.hasNext())
{
    RepositoryRow rr = (RepositoryRow) it.next();

    // Get recorded Event object and print information about it
    Id eventId = rr.getProperties().getIdValue(PropertyNames.ID);
    Event eventObject = Factory.Event.fetchInstance(os, eventId, null);
    String eventStatus = rr.getProperties().getInteger32Value(PropertyNames.EVENT_STATUS).equals(new Integer(0)) ?
            "Succeeded" : "Failed";
    System.out.println("Event Properties:\n" +
        "  Event ID: " + eventId.toString()+ "\n" +
        "  Event Name: " + eventObject.getClassName()+ "\n" +
        "  Date Created: " + rr.getProperties().getDateTimeValue(PropertyNames.DATE_CREATED)+ "\n" +
        "  Event Status: " + eventStatus
    );

    // Get source modified object and print name of object and date last modified.
    // If the modified object was not recorded, null returned.
    IndependentObject modifiedObject = (IndependentObject)rr.getProperties().getObjectValue(PropertyNames.SOURCE_OBJECT);
    if (modifiedObject!=null)
    {
        try {
            System.out.println("Source modified object: " +
                    "\n   Name: " + modifiedObject.getProperties().getStringValue(PropertyNames.NAME) +
                    "\n   Date object modified: " + modifiedObject.getProperties().getDateTimeValue(PropertyNames.DATE_LAST_MODIFIED) );
        }
        catch (EngineRuntimeException e)
        {
             System.out.println("Source modified object:\n  " + e.getMessage() );
        }
    }
    else 
    {
         // If the modified object was not recorded then neither was the original object
         System.out.println("There is no source modified or source original object recorded for this audited event." +
                            "\n=================================\n");
         continue;
    }

    // Get source original object and print name of object and date last modified.
    // If original object was not recorded, null returned.
    IndependentObject originalObject = (IndependentObject) rr.getProperties().getObjectValue(PropertyNames.ORIGINAL_OBJECT);
    if (originalObject!=null)
    {
        try {
            System.out.println("Source original object: " +
                    "\n   Name: " + originalObject.getProperties().getStringValue(PropertyNames.NAME) + 
                    "\n   Date object modified: " + originalObject.getProperties().getDateTimeValue(PropertyNames.DATE_LAST_MODIFIED) ); 
        }
        catch (EngineRuntimeException e)
        {
             System.out.println("Source original object: \n  " + e.getMessage() );
        }
        System.out.println("\n=================================\n");
    }
    else 
    {
        System.out.println("There is no source original object recorded for this audited event." +
                           "\n=================================\n");
    }
}

C# Example


// Build the SQL select statement
String sqlString = "SELECT " + PropertyNames.ID + "," + PropertyNames.DATE_CREATED + "," + PropertyNames.EVENT_STATUS +
        "," + PropertyNames.ORIGINAL_OBJECT + "," + PropertyNames.SOURCE_OBJECT;

// Construct SearchSQL object to be used in search operation
SearchSQL sql = new SearchSQL(sqlString + " FROM " + GuidConstants.Class_CheckinEvent + 
        " WHERE " + PropertyNames.DATE_CREATED + "> 20080401T080000Z" +
        " ORDER BY " + PropertyNames.DATE_CREATED);

// Execute the search operation
SearchScope ss = new SearchScope(os);
IRepositoryRowSet rrc = ss.FetchRows(sql, null, null, null);

// Iterate and print results
foreach (IRepositoryRow rr in rrc)
{
    IIndependentObject modifiedObject;
    IEvent eventObject;

    // Get recorded Event object and print information about it
    Id eventId = rr.Properties.GetIdValue(PropertyNames.ID);
    eventObject = Factory.Event.FetchInstance(os, eventId, null);
    String eventStatus = rr.Properties.GetInteger32Value(PropertyNames.EVENT_STATUS) == 0 ? "Succeeded" : "Failed";
    System.Console.WriteLine("Event Properties:\n" +
        "  Event ID: " + eventId.ToString() + "\n" +
        "  Event Name: " + eventObject.GetClassName() + "\n" +
        "  Date Created: " + rr.Properties.GetDateTimeValue(PropertyNames.DATE_CREATED)+ "\n" +
        "  Event Status: " + eventStatus
    );

    // Get source modified object and print name of object and date last modified.
    // If the modified object was not recorded, null returned.
    modifiedObject = (IIndependentObject)rr.Properties.GetObjectValue(PropertyNames.SOURCE_OBJECT);
    if (modifiedObject!=null)
    {
        try {
            System.Console.WriteLine("Source modified object: " +
                    "\n   Name: " + modifiedObject.Properties.GetStringValue(PropertyNames.NAME) +
                    "\n   Date object modified: " + modifiedObject.Properties.GetDateTimeValue(PropertyNames.DATE_LAST_MODIFIED) );
        }
        catch (EngineRuntimeException e)
        {
            System.Console.WriteLine("Source modified object:\n  " + e.Message );
        }
    }
    else 
    {
        // If the modified object was not recorded then neither was the original object
        System.Console.WriteLine("There is no source modified or source original object recorded for this audited event." +
                "\n=================================\n");
        continue;
    }

    // Get source original object and print name of object and date last modified.
    // If original object was not recorded, null returned.
    IIndependentObject originalObject = (IIndependentObject)rr.Properties.GetObjectValue(PropertyNames.ORIGINAL_OBJECT);
    if (originalObject!=null)
    {
        try {
            System.Console.WriteLine("Source original object: " +
                    "\n   Name: " + originalObject.Properties.GetStringValue(PropertyNames.NAME) + 
                    "\n   Date object modified: " + originalObject.Properties.GetDateTimeValue(PropertyNames.DATE_LAST_MODIFIED) ); 
        }
        catch (EngineRuntimeException e)
        {
            System.Console.WriteLine("Source original object: \n  " + e.Message );
        }
            System.Console.WriteLine("\n=================================\n");
    }
    else 
    {
         System.Console.WriteLine("There is no source original object recorded for this audited event." +
                  "\n=================================\n");
    }
}