Writing to event log and ULS in Sharepoint 2010

No logging means no visibility

Logging is a prime and most basic feature that any software need to have, and there are many ways to implement logging. In this post, I will illustrate a way to implement logging to windows application event log and/or the ULS when developing custom sharepoint 2010 solutions.

The assumption here is that the solution will be deployed as a farm solution.

A log entry can have various attributes or properties, but most likely we would want to use the following attributes:

  1. Source
  2. Severity
  3. Correlation ID
  4. Message

When writing to the application event log, it is advisable to create the event sources beforehand. Identify your event sources that you want to use and create these sources, one way is to use the script in this post.

Typically I would advise against logging to the application event log on production systems, for sharepoint solutions, use the ULS, but alas, reading ULS every time things goes wrong can be time consuming, yeah we have great tools like the ULS viewer, but it is still not as easy to read as the event viewer. Therefore, I typically write my logs to the application event log on my DEV machine.

For this, I need a switch that enables me to direct all my log entries to the application event log, the ULS, or both (or other sources). Astute .NET developers would probably think of Enterprise Library when it comes to this scenario. Yes EntLib is collection of great components, but if all we want is a simple logger that writes to two sources, we might as well write common logic and be done in 30 minutes or less rather than trying to bake in and configure EntLib, think about the web.config changes you have to do with EntLib.

For me, my switch is simply a couple of constants in a static class:

namespace Department.Solution.Common
{
    public static class Constants
    {
        public static class AppSettings
        {
            public const bool LOG_TRACE = true;
            public const bool LOG_EVENTLOG = true;
        }
    }
}

We should also ideally put our event sources in constants:

namespace Department.Solution.Common
{
    public static class Constants
    {
        public static class LogSource
        {
            public const string LOGSRC_COMMON = "ProductXYZ Common";
            public const string LOGSRC_WEBPART = "ProductXYZ WebParts";
        }
    }
}

Then I have these methods in my common logger class to write to event log and/or the ULS depending on those flags:

private static void WriteToTrace(Guid correlationGuid, string source, string message, EventLogEntryType entry)
{
    if (!Constants.AppSettings.LOG_TRACE) return;
    var diagSvc = SPDiagnosticsService.Local;
    var traceSeverity = TraceSeverity.Unexpected;
    var evtSeverity = EventSeverity.Error;

    switch (entry)
    {
        case EventLogEntryType.Information:
        case EventLogEntryType.FailureAudit:
        case EventLogEntryType.SuccessAudit:
            traceSeverity = TraceSeverity.Verbose;
            evtSeverity = EventSeverity.Information;
            break;
        case EventLogEntryType.Warning:
            traceSeverity = TraceSeverity.Medium;
            evtSeverity = EventSeverity.Warning;
            break;
        default:
            traceSeverity = TraceSeverity.Unexpected;
            evtSeverity = EventSeverity.Error;
            break;
    }
    SPSecurity.RunWithElevatedPrivileges(() =>
        diagSvc.WriteTrace(0,
        new SPDiagnosticsCategory(source, traceSeverity, evtSeverity), traceSeverity,
        "Correlation Guid {0}: {1}", new object[] { correlationGuid.ToString("B"), message })
    );
}

private static void WriteEventLogEntry(Guid correlationGuid, string source, string message, EventLogEntryType entry)
{
    if (!Constants.AppSettings.LOG_EVENTLOG) return;
    SPSecurity.RunWithElevatedPrivileges(() =>
            {
                var appLog = new EventLog { Source = source };
                appLog.WriteEntry(string.Format("Correlation Guid {0}{1}{2}", correlationGuid.ToString("B"), System.Environment.NewLine, message), entry);
            });
}

Simply put wrappers and overrides around these two methods, for example:

public static string LogError(string source, string format, params object[] args)
{
    return LogError(source, string.Format(format, args));
}
public static string LogError(string source, string message)
{
    Guid correlationGuid = Guid.NewGuid();
    WriteEventLogEntry(correlationGuid, source, message, EventLogEntryType.Error);
    WriteToTrace(correlationGuid, source, message, EventLogEntryType.Error);
    return correlationGuid.ToString();
}

Then whenever you want to log anything, simply call the public method and pass in your source string in one line (for example, if the logger is implemented within your custom LoggerInstance class):

LoggerInstance.LogError(Constants.LogSource.LOGSRC_WEBPART, "Woops, web part {0} errored out!", webPart.Title);

Logging would be a breeze if you have this helper class, because it simply provide a layer of abstraction.

Posted on November 11, 2011, in Sharepoint and tagged , , , , . Bookmark the permalink. Leave a comment.

Leave a comment