Using SharePoint 2010’s Logging Infrastructure, Part 3

Update 2011-02-03: Fixed links to part 1 and 2.

This is the third and final part of my post about using SharePoint 2010’s logging infrastructure. Part one explained the basics of SharePoint 2010’s logging infrastructure. Then part two showed how to write to SharePoint’s log. This part shows how to write to Windows event logs.

Writing to Windows event logs

As you know from the first part of this post or already found out yourself simply calling SPDiagnosticsService.Local.WriteEvent() to write to Windows event logs will result in this error message:

SecurityException: The source was not found, but some or all event logs could not be searched. Inaccessible logs: Security.

That means:

  1. You have to register a source.
  2. Probably to support filtering across multiple logs Microsoft tries to find the source in every log and not just the one you are trying to write to. And searching the security log seems to be verboten.

Registering an event source should be done in a feature receiver as you only have to do it once and the account doing it needs the appropriate permissions. Of course you have to register that source on every server that uses the new logging component–which will probably be every server in the farm.

The code below shows the event receiver from part two of this post extended appropriately. Of course you’ll want to use the same name you specified for your SharePoint log’s area as the name for the source. This program will write to the Application log.

using System;
using System.Runtime.InteropServices;
using LogTest.WebPart1;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.Win32;

namespace LogTest.Features.Feature1 {
 [Guid("3f799d60-01e7-478b-a63b-07c212c00b0f")]
 public class Feature1EventReceiver : SPFeatureReceiver {
  private const string _applicationEventLogRegistryKeyPath =
   @"SYSTEM\CurrentControlSet\services\eventlog\Application";
  private const string _wssServicesRegistryKeyPath =
   @"SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\14.0\WSS\Services";

  public override void FeatureActivated(SPFeatureReceiverProperties properties) {
   //Debugger.Break();
   foreach (SPServer server in properties.Definition.Farm.Servers) {
    if (server.Role != SPServerRole.Invalid) {
     // Register source for Application event log
     RegistryKey hklmRegistryKey = RegistryKey.OpenRemoteBaseKey(
      RegistryHive.LocalMachine, server.Address);
     RegistryKey applicationEventLogRegistryKey = hklmRegistryKey.OpenSubKey(
      _applicationEventLogRegistryKeyPath, true);
     RegistryKey mySourceRegistryKey = applicationEventLogRegistryKey.OpenSubKey(
      MyDiagnosticsService.AreaName);
     if (mySourceRegistryKey == null) {
      mySourceRegistryKey = applicationEventLogRegistryKey.CreateSubKey(
       MyDiagnosticsService.AreaName,
       RegistryKeyPermissionCheck.ReadWriteSubTree);
      //HACK: hard coded path and name of resource DLL
      mySourceRegistryKey.SetValue("EventMessageFile",
       @"C:\Windows\Microsoft.NET\Framework\v2.0.50727\EventLogMessages.dll",
       RegistryValueKind.String);
     }
     // Create Registry key for integrating with SharePoint's Central Administration 
     // ...
    }
   }
  } // FeatureActivated()

  public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
   //Debugger.Break();
   foreach (SPServer server in properties.Definition.Farm.Servers) {
    if (server.Role != SPServerRole.Invalid) {
     // Unregister source for Application event log
     RegistryKey hklmRegistryKey = RegistryKey.OpenRemoteBaseKey(
      RegistryHive.LocalMachine, server.Address);
     RegistryKey applicationEventLogRegistryKey = hklmRegistryKey.OpenSubKey(
      _applicationEventLogRegistryKeyPath, true);
     RegistryKey mySourceRegistryKey = applicationEventLogRegistryKey.OpenSubKey(
      MyDiagnosticsService.AreaName);
     if (mySourceRegistryKey != null) {
      applicationEventLogRegistryKey.DeleteSubKey(MyDiagnosticsService.AreaName);
     }
     // - Remove Registry key for integrating with SharePoint's Central Administration 
     // ...
    }
   }
  } // FeatureDeactivating()
 } // class Feature1EventReceiver
} // namespace LogTest.Features.Feature1

Now let’s extend MyDiagnosticsService’s LogMessage() method to write to Windows event logs, too:

public void LogMessage(ushort id, MyDiagnosticsCategory myDiagnosticsCategory,
 EventSeverity eventSeverity, TraceSeverity traceSeverity, 
 string message, params object[] data) {
 SPDiagnosticsCategory category 
  = Local.Areas[AreaName].Categories[myDiagnosticsCategory.ToString()];
 if (eventSeverity != EventSeverity.None) {
  // eventSeverity==EventSeverity.None would cause an ArgumentException:
  // "Specified value is not supported for the severity parameter."
  // Will also write to SharePoint's log
  Local.WriteEvent(id, category, eventSeverity, message, data);
 }
 else {
  if (traceSeverity != TraceSeverity.None) {
   // traceSeverity==TraceSeverity.None would cause an ArgumentException:
   // "Specified value is not supported for the severity parameter."
   Local.WriteTrace(id, category, traceSeverity, message, data);
  }
 }
} // LogMessage()

And finally the revised testing code:

void LogMessageButton_Click(object sender, EventArgs e) {
 foreach (MyDiagnosticsService.MyDiagnosticsCategory dc
  in Enum.GetValues(typeof(MyDiagnosticsService.MyDiagnosticsCategory))) {
  MyDiagnosticsService.Local.LogMessage(MyDiagnosticsService.EventID, dc,
   EventSeverity.None, TraceSeverity.None, "Just a test.", null);
  MyDiagnosticsService.Local.LogMessage(MyDiagnosticsService.EventID, dc,
   EventSeverity.Verbose, TraceSeverity.Verbose, "Just a test.", null);
  MyDiagnosticsService.Local.LogMessage(MyDiagnosticsService.EventID, dc,
   EventSeverity.Error, TraceSeverity.High, "Just a test.", null);
  MyDiagnosticsService.Local.LogMessage(MyDiagnosticsService.EventID, dc,
   EventSeverity.ErrorCritical, TraceSeverity.Unexpected, "Just a test.", null);
 }
} // LogMessageButton_Click()

Here’s a screenshot showing the result–even more entries cluttering your Windows event log :-).

Now that you’ve solved the technical problems it’s time to become creative and create a better API for your DiagnosticsService. Instead of having a single method accepting any combination of Event and Trace level values you’ll want to provide specialized methods for example to log diagnostic messages and exceptions. These methods will use hard coded Event and Trace levels so the caller has to provide only the actual message.

And of course you’ll have to invent categories–functional ones like SharePoint uses or technical ones like solution names. Happy inventing.

This entry was posted in Uncategorized and tagged . Bookmark the permalink.

5 Responses to Using SharePoint 2010’s Logging Infrastructure, Part 3

  1. Pingback: Using SharePoint 2010′s Logging Infrastructure, Part 2 | Dieter Bremes's Blog

  2. Alan says:

    Thanks for this great post. I didn’t really want to create a custom event source, so I tried to use SharePoint’s one by:


    var category = SPDiagnosticsService.Local.Areas
    .FirstOrDefault(a => a.Name == "SharePoint Foundation")
    .Categories.FirstOrDefault(c => c.Name == "Runtime");

    I can retrieve this category, but I’m getting the security exception you have shown on the part 3 of your post. I’ve tried added the app pool account to “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Security” and giving it “Read” permission, and the error message changes to:

    System.Security.SecurityException: Requested registry access is not allowed.

    Any idea what could be the cause?

    • dbremes says:

      Doesn’t sound like the exception is related to not being allowed to search the security log. If you can’t get a full stack trace you could either use Reflector Pro and break on the exception or monitor registry access with Process Monitor.

      But to be honest simply implementing a custom event source is much less hassle. You also get the additional benefit of being able to filter on your messages. And I wouldn’t rely on everybody using the correct trace and event severity values but prefer to have them call our custom methods.

      Good luck with your project whichever route you decide to follow.

      • Neeraj says:

        Hi,
        I applied all the changes mentioned in this blog, but i am getting and SQL exception “The EXECUTE permission was denied on the object ‘proc_putObject’, database ‘abc_Config’, schema ‘dbo'”

  3. Shamshad Ali says:

    dbremes,
    I did not get all from your last reply above. Could you plz. make a sample code and post here, I need to implement this as you said but how, plz. help.
    Here is my post on MSDN:
    http://social.msdn.microsoft.com/Forums/en-US/sharepoint2010programming/thread/9f4ab240-468e-4fd6-8499-2fe23a92498d

    Please help…

    Shamshad Ali

Leave a comment