Sunday, October 30, 2011

SPAudit: How to read from the audit log

As part of my series on SharePoint auditing, I will demonstrate how to read from the audit log. This is useful if you are restricted to Windows SharePoint Service or SharePoint Foundation, where the out-of-the-box reports are not available, or just need to make your own custom reports.

The auditing is centered around the SPAudit class, which is available in all versions of SharePoint 2007 and SharePoint 2010, except in sandboxed solutions.

How do you read the audit log?

SharePoint has a number of built-in autid log reports. These are made available when activating the Reporting feature, and thus cannot be used in Windows SharePoint Service (WSS) and SharePoint Foundation. Once enabled, the audit reports are accessible from the link 'Audit log reposts' in the 'Site Collection Settings' column. Any audit events you create yourself may be viewed through the 'Run a custom report' link. Either way, you are presented with an Excel spreadsheet with the audit data. This may or may not be what you were looking for.

There are several scenarios where you could benefit from reading directly from the audit log. Some of these include:

  • You are restricted to WSS or SharePoint Foundation. In this case you do not have any GUI for the audit log, but the functionality is still there ready to use.
  • You record custom events to the audit log, and wish to process these at a later time, for example in a timer job.
  • You are not satisfied with the out-of-the-box reports, and wish to display your own selection of events.

The two central classes when reading from the audit log are the SPAuditQuery and the SPAuditEntry classes.

Reading the audit log with SPAuditQuery

If you need to get the entries from the audit log, then SPAuditQuery is the class to use. The audit log exists on a site collection basis, and therefore it makes sense to instantiate the SPAuditQuery with a site collection, for example by using the current context:

SPAuditQuery query =
  new SPAuditQuery(SPContext.Current.Site);

This of course would not work in a timer job - here you have to get your site collection object in some other way.

Next step is to set up filters for our query. We generally don't want to get all the entries in the audit log at once. We have several means of adding filters to the query:

  • RestrictToList: Restricts the query to events relevant to the specified list.
  • RestrictToListItem: Restricts the query to events that are relevant to the specified list item.
  • RestrictToUser: Restricts the query to events relevant to the specified user.
  • RowLimit: Restricts the query to a maximum number of returned entries. Warning: If using SharePoint 2007 please read the section A snake in the garden below.
  • SetRangeStart: Restricts the query to entries on or after a specified time.
  • SetRangeEnd: Restricts the query to entries on or before a specified time.
  • AddEventRestriction: Restricts the query to the specified type of event (using the SPAuditEventType enum). This may be done multiple times to query many event types at the same time. If this method is not called, the query will return entries for all event types.

Once you have created your audit query, the next thing is to run it to get the audit entries. This is done in the following way:

SPAuditEntryCollection entries =   SPContext.Current.Site.Audit.GetEntries(query);

Understanding the audit log entries

Once you have executed your query, you end up with a SPAuditEntryCollection, which of course is a collection of SPAuditEntry instances. Each of these instances holds information of a specific audit event, which are defined through the properties of the SPAuditEntry instance. The MSDN article describes these in detail, but I would like to highlight a few of them:

  • Event: What actually happened? Was a field deleted? A member added to a group? What? This is exposed through a value of the SPAuditEventType enumeration.
  • EventName: A string that gives the name of the event - but only when the event type is SPAuditEventType.Custom. So this is how you distinguish your own events.
  • ItemType: Which type of object did the event occur on? A list? A list item? A content type? This property used the SPAuditItemType enumeration.
  • ItemId: This specifies the exact object the event occurred on. The ID (which is a Guid) is unique across the whole site collection. But if the event has recorded a deletion you will not be able to retrieve extra information on the object (because - you know - it has been deleted).
  • Occurred: When did the event occur.
  • UserId: The integer ID of the user who caused the event. The actual user may be retrieved by SPWeb.SiteUsers.GetByID(userId). Note that the user may have been deleted in the mean time.
  • EventData: A detailed review of what happened. Which group was the user added to? What field was updated? Which list was deleted? The event data is presented as XML. The MSDN article has an overview of the XML for the different event types. If you create you own event types, you would do well in using the same XML tags (groupid for the group ID, user for the uer ID etc.) as used for the standard events. This is just a recommendation that will make it easier for you to parse the XML, not a requirement. You can even ignore the XML format, and just write a string with a friendly description of the event. Just remember that you generally don't need to store information that is already present in the content database. For example, you would not need to store a user's name, email, phone number etc. in the audit log, since you can get them easily just by having the user ID.

A snake in the garden

There is a snake in the garden of SharePoint 2007: The property RowLimit was added to the SPAuditQuery class somewhere between Service Pack 1 and 2. When developing a solution for SharePoint 2007 we either have to require that Service Pack 2 is installed, or handle the RowLimit property with care.

The following code - which uses the System.Reflection namespace - takes care of the problem: Using reflection, we first check if the property exists, before we set it.

private void SetRowLimit(SPAuditQuery query)
{
  Type type = query.GetType();
  PropertyInfo propInfo = type.GetProperty("RowLimit");
  if (propInfo != null)
  {
    propInfo.SetValue(query, myRowLimit, null);
  }
}

Try it youself

If you have read my other auditing posts How to turn auditing on programmatically and How to write to the audit log there should be nothing stopping you from starting using the SharePoint audit log already today. Have fun.

5 comments:

  1. Hi Thomas,

    Not sure if you are still active here but here goes.
    I am an absolute beginner to sharepoint development and your posts on auditing have been very informative and helpful.Quick question though:In the beginning of the article you say"All you have to do is add the following line..".But my question is, add it where?

    ReplyDelete
  2. Hello Kannan
    Yes, I am still active, too much active in fact. But I hope to soon have more time to devote to my blog.

    As for your question, the answer is: Whereever you need it. Something happens, and you want to log it. It could be in an event receiver on a list, in the click event of a web part button, or perhaps as part of a custom navigation provider. The only requirement is that you are able to run code when that 'something' happens.

    ReplyDelete
  3. I wish that I had discovered this post earlier - I have been discovering this stuff the painful way. I have one question

    I am writing a Console App to run on a client's SharePoint server to read entries and store them in a proprietary database for ease of retrieval (I have previously done something similar in MOSS 2007 that read the text log files.

    How do we know how many entries, or for what period these entries go back.

    ReplyDelete
  4. Hello,

    Thanks for your article. it is useful. Could you explain how to write a custom auditing event by an example?
    Thanks.

    ReplyDelete
  5. Thanks, I like this post which provides good information to view audit log reports. I have used this SharePoint auditing tool(http://www.lepide.com/sharepoint-audit/) that assists to view who is taking what action on which content in sites collection and view the complete data in the audit logs for a specific site collection.

    ReplyDelete