Implementing logging in SharePoint 2010 using the SharePoint Guidance library

By bhoeijmakers at December 04, 2011 08:18
Filed Under: .NET, SharePoint 2010

In this post I will talk about a way to implement a logging component in SharePoint 2010 that uses the Microsoft SharePoint Guidance Patterns and Practices library. The SharePoint Guidance library contains a lot of cool and very useful components. I will only focus on the logging component in this post and how to use it in practice to create your own logging component. In this post I am creating a Farm Solution. In a later post I will talk about how to do this in a sandboxed solution.

 

If you don’t want to read the theory and go straight to downloading the example solution, click here

 

 

The SharePoint Service Locator

The SharePoint Guidance library uses an implementation of the Service Locator design pattern for it’s components called the SharePoint Service Locator. For detailed information about the SharePoint Service Locator, read this.

 

MSDN says the following:

The SharePoint service locator is a reusable component that you can include in your own SharePoint applications. It allows you to decouple consumers of an interface from the implementations of that interface. Instead of creating an object by invoking the constructor of a class, you request an object with a specified interface from the service locator.

 

In the rest of this post I am assuming that you know how the SharePoint Service Locator works.

 

In SharePoint 2010 the ILogger interface is mapped by default to the SharePointLogger class. This is a basic implementation that supports logging to the ULS as well as logging to the Event Viewer. I’ll be using the default mapping in this post, but the Service Locator pattern makes it very easy to plug in your own more advanced implementation of the ILogger interface if you need it. In my experience the default implementation is more than enough for most projects.

 

Setting up the Visual Studio Solution

I’ve created a new Visual Studio 2010 project and selected the Empty SharePoint Project template. I downloaded the SharePoint Guidance 2010 library from the link provided in the introduction of this post. I added the Microsoft.Practices.SharePoint.Common project provided in the download to my SharePoint project. Then I referenced the Microsoft.Practices.SharePoint.Common project from my newly created SharePoint project. All set up and ready to go!

 

Note: You might not want to include the Microsoft.Practices.SharePoint.Common project in all your solutions that use the SharePoint Guidance 2010 library. Alternatively you could create a separate WSP that deploys just the Guidance 2010 Library, or deploy just the DLL with your solution. When creating solutions for customers, I usually deploy the Guidance Library in a separate WSP that has to be deployed to the farm once.

 

I then added a LogHandler class that will contain most of the code.

 

image

 

 

 

Logging to the ULS

I want my logging component to be able to log to the ULS. If we use ULS Viewer to view our log (you ARE using ULS Viewer, right??), it shows us something like this (click to enlarge):

 

image

 

As you can see in the image, an ULS entry contains (besides other information), Category, EventID, Level and Message columns. I want the logging component we are creating to use default values for Category, EventID and Level that can be overridden by the user. It is good practice to use your own category and area, and not reuse the standard Microsoft ones. This way IT-pro’s can distinguish between messages SharePoint spits out and messages from custom solutions.

 

The default values for my EventID, Category and Area I store in a class called ConstValues. Here is what it looks like:

 

    public class ConstValues
    {
        public const int EVENTID = 9999;
        public const string CATEGORY_DEFAULT = "InsideSharePoint.NET";
        public const string LOGAREA_DEFAULT_ERROR = "Custom Error";
        public const string LOGAREA_DEFAULT_WARNING = "Custom Warning";
        public const string LOGAREA_DEFAULT_VERBOSE = "Custom Message";
    }

 

We have to register his category in SharePoint before it can be used. To do that I’ve created a Farm scoped feature with a Feature Receiver attached. In this Feature Receiver the Category is registered in the Farm.

 

public class DiagnosticsRegistrationReceiver : SPFeatureReceiver
{
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        // recreate the area & categories
        LogConfigurationHelper.RemoveArea(ConstValues.AREA_DEFAULT);
 
        // create the area
        DiagnosticsArea newArea = new DiagnosticsArea(ConstValues.AREA_DEFAULT);
 
        // for each loglevel, add a category to the area
        newArea.DiagnosticsCategories.Add(
            new DiagnosticsCategory(ConstValues.LOGCATEGORY_DEFAULT_ERROR, 
                Microsoft.SharePoint.Administration.EventSeverity.Error, 
                Microsoft.SharePoint.Administration.TraceSeverity.Unexpected));
            
        newArea.DiagnosticsCategories.Add(
            new DiagnosticsCategory(ConstValues.LOGCATEGORY_DEFAULT_WARNING, 
                Microsoft.SharePoint.Administration.EventSeverity.Warning, 
                Microsoft.SharePoint.Administration.TraceSeverity.Medium));
 
        newArea.DiagnosticsCategories.Add(
            new DiagnosticsCategory(ConstValues.LOGCATEGORY_DEFAULT_VERBOSE, 
                Microsoft.SharePoint.Administration.EventSeverity.Verbose, 
                Microsoft.SharePoint.Administration.TraceSeverity.Verbose));
        LogConfigurationHelper.AddArea(newArea);
 
        DiagnosticsAreaEventSource.EnsureConfiguredAreasRegistered();
    }
 
    public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
        LogConfigurationHelper.RemoveArea(ConstValues.AREA_DEFAULT);
    }
}

 

The Feature Receiver makes use of the LogConfigurationHelper class that contains methods for handling DiagnosticsAreas. This contents of this class was copied directly from the SharePoint 2010 Guidance documentation  here. All I did was put the methods together in one helper class. Check out my example solution for a complete implementation of the class.

 

As you can see I declare a single DiagnosticsArea that contains different areas for Error, Warning and Verbose log messages. The DiagnosticsAreaEventSource.EnsureConfiguredAreasRegistered() method ensures that the DiagnosticArea is rgistered as Event Source on all servers in the Farm.

 

The wrapper class

Now I setup the LogHandler class, putting in all the methods I need for logging. My implementation logs everything to the ULS. You might want to change it to log Errors to the Event Viewer also, depending on the requirements of the IT-pro department.

 

public static class LogHandler
{
/// <summary>
/// log an error using the default values for eventId, category and area
/// </summary>
/// <param name="ex"></param>
public static void LogError(Exception ex, string additionalMessage)
{
    Log(ex, additionalMessage, ConstValues.EVENTID,
        Microsoft.SharePoint.Administration.TraceSeverity.Unexpected,
        ConstValues.LOGCATEGORY_DEFAULT_ERROR, ConstValues.AREA_DEFAULT);
}
/// <summary>
/// Log a warning using the default category/area settings
/// </summary>
/// <param name="message"></param>
public static void LogWarning(string message)
{
    LogWarning(message, ConstValues.EVENTID, ConstValues.LOGCATEGORY_DEFAULT_WARNING, ConstValues.AREA_DEFAULT);
}
/// <summary>
/// Log a warning using the given category/area settings
/// </summary>
/// <param name="message">Message to be logged</param>
/// <param name="eventId">EventId under which to log</param>
/// <param name="category">Category to use for logging</param>
/// <param name="area">Area to use for logging</param>
public static void LogWarning(string message, int eventId, string category, string area)
{
    string logToCategory = category;
    string logToArea = area;
    var logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
    Log(message, eventId,
        Microsoft.SharePoint.Administration.TraceSeverity.Medium,
        category, 
        area);
}
/// <summary>
/// Log a verbose message to the log using the default settings for category/area
/// </summary>
/// <param name="message"></param>
public static void LogVerbose(string message)
{
    LogVerbose(message, ConstValues.EVENTID, ConstValues.LOGCATEGORY_DEFAULT_WARNING, ConstValues.AREA_DEFAULT);
}
/// <summary>
/// Log a verbose message to the log using the given category/area settings
/// </summary>
/// <param name="message">The message to be logged</param>
/// <param name="eventId">The eventId under which to log</param>
/// <param name="category">Category used for logging</param>
/// <param name="area">Area to use for logging</param>
public static void LogVerbose(string message, int eventId, string category, string area)
{
    var logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
    Log(message, eventId,
        Microsoft.SharePoint.Administration.TraceSeverity.Verbose,
        category,
        area);
}
internal static void Log(Exception ex, string additionalMessage,
    int eventId, Microsoft.SharePoint.Administration.TraceSeverity severity,
    string category, string area)
{
    var logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
    logger.TraceToDeveloper(ex, additionalMessage, eventId, severity, string.Format("{0}/{1}", category, area));
}
internal static void Log(string message,
    int eventId, Microsoft.SharePoint.Administration.TraceSeverity severity,
    string category, string area)
{
    var logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
    logger.TraceToDeveloper(message, eventId, severity, string.Format("{0}/{1}", category, area));
}

 

What is interesting is the category parameter of the ILogger.TraceToDeveloper method. It is of the type string, but takes a string in a special format, being category/area.

That is it. Click the following link to download the complete Visual Studio 2010 Solution.

 

Download the complete solution

 

Summary

In this post I showed how to implement a logging component for real world solutions using the default implementation of the ILogger interface of the SharePointServiceLocator. I’ve showed you how to configure diagnostics areas and how to implement a static wrapper class for logging.

Comments (4) -

12/4/2011 9:37:19 PM #

Hi, your IT-Pro colleague here Smile

Please keep in mind the permissions needed to write to trace and event logs. Membership of the local group "Performance Log Users" is required. While this is not specific to ILogger, it is often overlooked. Especially the service accounts for the Application Pools of the Web Applications.

Martijn Schouten Netherlands | Reply

12/6/2011 7:00:15 AM #

Yes, good call. When your messages are not showing up in the ULS, probably the application pool account of the web application is not in the "Performance Log Users" group.

bhoeijmakers Netherlands | Reply

2/10/2012 10:24:57 AM #

Hi

I could able to Log Error
But Log Verbose i snot working

How to Log Trace using the Guiodance Library

Thanks
Vijay

vijay India | Reply

2/13/2012 9:46:47 AM #

Verbose logging is not "turned on" by default in SharePoint 2010. To see Verbose messages in your trace (ULS) log, turn it on in Central Administration. Go to Central Admin -> Monitoring -> Configure diagnostic logging and set the "Least critical event to report to the trace log" for the category you created to Verbose. Watch out, If you turn on Verbose logging for other categories than your own this has the tendency to generate lots and lots of logging. Great for troubleshooting, not so great if you don't need it.

hope that answers your question
Bart-Jan

bhoeijmakers Netherlands | Reply

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


About the author

Bart-Jan Hoeijmakers is Lead SharePoint Developer at VX Company. Since 2006 he is the driving force behind the development of several enterprise SharePoint projects within VX Company. He is a hardcore developer in both SharePoint and ASP.NET and loves to dive into the dark sides of SharePoint development.

Month List