Sunday, 5 August 2012

SharePoint 2010 : ULS Logging


“How do you do logging in your application?” This is the question many people asks me. Hence thought to put it into a post and redirect them to it…

SharePoint 2010 Guidance has one approach to log the exception into ULS logs…however approach needs a farm scoped feature to register ULS Categories and Areas. Many times, creating farm level feature is not possible due to the restrictions applied by Farm Administrators.

Figure 1 : Area and Category in ULS log file

There is another approach mentioned by my favorite MVP Waldek Mastykarz in his blog post Logging to ULS inSharePoint 2010.  This approach does not need any feature for registering the ULS Category and Areas.

Well, I use Waldek’s approach with little bit of tweaks so that I can to log Exceptions, Information and Warnings. I also use Enums for defining Categories which represents particular module so that I would come to know in which module exceptions has occurred. I use Application name as Area and Module names as Categories....


public class EnumUtil
{
    public static string StringValueOf(Enum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());
        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attributes.Length > 0)
        {
            return attributes[0].Description;
        }
        else
        {
            return value.ToString();
        }
    }

    public static object EnumValueOf(string value, Type enumType)
    {
        string[] names = Enum.GetNames(enumType);
        foreach (string name in names)
        {
            if (StringValueOf((Enum)Enum.Parse(enumType, name)).Equals(value))
            {
                return Enum.Parse(enumType, name);
            }
        }

        throw new ArgumentException("The string is not a description or value of the specified enum.");
    }

    public static IEnumerable<T> GetValues<T>()
    {
        return Enum.GetValues(typeof(T)).Cast<T>();
    }
}

public enum LoggingCategory
{
    [DescriptionAttribute("Application Pages")]
    ApplicationPages,

    [DescriptionAttribute("Event Receiver - Announcements list")]
    AnnouncementEventReceivers,

    [DescriptionAttribute("Feature Receiver - Master Page Applier")]
    MasterPageApplierFeatureReceiver,

    [DescriptionAttribute("WebPart - HomePage")]
    HomePageWebPart,

    [DescriptionAttribute("WebPart - Announcements")]
    AnnouncementsWebPart,

    [DescriptionAttribute("WebPart - Calendar")]
    CalendarWebPart,

    [DescriptionAttribute("Module - Product Order")]
    ProductOrderModule
}

public class LoggingService : SPDiagnosticsServiceBase
{
    static readonly object padlock = new object();

    public static string diagnosticAreaName = "SharePoint Custom Development";
    private static LoggingService _Current;
    public static LoggingService Current
    {
        get
        {
            lock (padlock)
            {
                if (_Current == null)
                {
                    _Current = new LoggingService();
                }
                return _Current;
            }
        }
    }

    private LoggingService()
        : base("SharePoint Custom Development Logging Service", SPFarm.Local)
    {
    }

    protected override IEnumerable<SPDiagnosticsArea> ProvideAreas()
    {
        List<SPDiagnosticsCategory> categories = new List<SPDiagnosticsCategory>();

        //Unexpected Errors
        foreach (var category in EnumUtil.GetValues<LoggingCategory>())
        {
            categories.Add(new SPDiagnosticsCategory(EnumUtil.StringValueOf(category) + " Error", TraceSeverity.Unexpected, EventSeverity.Error));
        }

        //Information (verbose)
        foreach (var category in EnumUtil.GetValues<LoggingCategory>())
        {
            categories.Add(new SPDiagnosticsCategory(EnumUtil.StringValueOf(category) + " Information", TraceSeverity.Verbose, EventSeverity.Verbose));
        }

        //Warnings
        foreach (var category in EnumUtil.GetValues<LoggingCategory>())
        {
            categories.Add(new SPDiagnosticsCategory(EnumUtil.StringValueOf(category) + " Warning", TraceSeverity.Medium, EventSeverity.Warning));
        }

        List<SPDiagnosticsArea> areas = new List<SPDiagnosticsArea>
            {
                new SPDiagnosticsArea(diagnosticAreaName, categories)
            };

        return areas;
    }

    /// <summary>
    /// Log Errors and exceptions
    /// </summary>
    /// <param name="categoryEnumValue">LoggingEnum value representing the ULS category.</param>
    /// <param name="exception">Exception to be logged</param>
    public static void LogError(LoggingCategory categoryEnumValue, Exception exception)
    {
        if (exception is ApplicationException && exception.InnerException != null)
        {
            exception = exception.InnerException;
        }

        string errorLogString = string.Format("Message: " + exception.Message + Environment.NewLine + "StackStrace: " + exception.StackTrace);
        string categoryName = EnumUtil.StringValueOf(categoryEnumValue);

        SPDiagnosticsCategory category = LoggingService.Current.Areas[diagnosticAreaName].Categories[categoryName + " Error"];
        LoggingService.Current.WriteTrace(00, category, TraceSeverity.Unexpected, errorLogString);
    }

    /// <summary>
    /// Log Informative messages
    /// </summary>
    /// <param name="categoryEnumValue">LoggingEnum value representing the ULS category.</param>
    /// <param name="infoMessage">Message to be logged</param>
    public static void LogInfo(LoggingCategory categoryEnumValue, string infoMessage)
    {
        string categoryName = EnumUtil.StringValueOf(categoryEnumValue);
        SPDiagnosticsCategory category = LoggingService.Current.Areas[diagnosticAreaName].Categories[categoryName + " Information"];
        LoggingService.Current.WriteTrace(00, category, TraceSeverity.Verbose, infoMessage);
    }

    /// <summary>
    /// Log warnings messages
    /// </summary>
    /// <param name="categoryEnumValue">LoggingEnum value representing the ULS category.</param>
    /// <param name="warningMessage">Message to be logged</param>
    public static void LogWarning(LoggingCategory categoryEnumValue, string warningMessage)
    {
        string categoryName = EnumUtil.StringValueOf(categoryEnumValue);
        SPDiagnosticsCategory category = LoggingService.Current.Areas[diagnosticAreaName].Categories[categoryName + " Warning"];
        LoggingService.Current.WriteTrace(00, category, TraceSeverity.Medium, warningMessage);
    }
}


SPDiagnosticsServiceBase class used is not supported by Sandbox solutions...but Full Trust proxies can be used in that case...you can find approach and implementation here http://sandbox.codeplex.com/

I hope you will find it helpful.

Wish everybody a happy friendships day…time to go home to end todays fasting…

1 comment:

  1. Hi Parwej,

    How are you? Nice posts and in depth blogging.
    Thanks for sharing .

    Swati

    ReplyDelete