The best applications contain a good measure of logging to assist developers in tracking down errors when things don't go as they were designed or when unexpected things occur. SharePoint 2007 offered developers tools that were available in ASP.NET, such as the debug and trace log.

Developers could also write to the SharePoint log, known as the Unified Logging Service (ULS), but it required a little extra work to write directly to the log files. In SharePoint 2010, Microsoft has added additional logging and debugging capabilities to SharePoint 2010 to help developers include additional monitoring and troubleshooting in their custom applications.

Every SharePoint 2007 server wrote logging information to the ULS log files and server event log. The ULS log files, found in the \\{SharePoint Root\\}\LOGS folder on each SharePoint server in the farm, could contain quite a bit of information depending how the diagnostic log throttling was configured. While more logging detail is usually favored over less detail, it can be challenging to find a specific entry in the log files. SharePoint 2010 improves on this task of finding specific log entries by assigning some uniqueness to each log entry.

This uniqueness, called a correlation token, is a GUID that is presented to the user when an error occurs. An administrator or developer could then take this GUID string in the error and use it in searching for a specific entry in the log file. This will speed the process of finding entries in the log files in tracking down specific incidents.

SharePoint Logging Database
Another change Microsoft introduced is a new database that contains all logging information for all servers in the farm. This is done through the use of a few new timer jobs. Once these jobs are enabled (they are all disabled by default), SharePoint will insert diagnostic logging data into a new SQL Server database.

One of the biggest advantages to enabling these jobs is that all the various logs are aggregated into a single location that can be queried from various sources. The contents of this logging database could even be exposed using Business Connectivity Services (BCS) external content types and lists.

Reading the SharePoint ULS Logs
Aside from the new logging database in SharePoint 2010, developers can also analyze the raw SharePoint log files created in the \\{SharePoint Root\\}\LOGS folder on each server. When developing solutions, developers typically work in an isolated environment where everything is installed on a single server rather than a farm consisting of various servers. The log files are simply large fixed width delimited text files that can be read & searched using any text editor.

Aside from searching raw text files, quite a few developers have published various utilities that assist in reading the SharePoint log files. My favorite is the ULS Viewer on MSDN Code Gallery.

Writing to the SharePoint ULS Logs
Reading from the SharePoint ULS logs is helpful when troubleshooting SharePoint generated messages, but one thing developers really need is the ability to write their own messages to the log files. Microsoft made it much easier to write to the log files in SharePoint 2010 using the SPDiagnosticsService class:

try\\{
  ...

\\} catch (Exception ex) \\{

  SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("CPT", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, ex.Message, ex.StackTrace);
\\}

This snippet will write the exception details to the SharePoint ULS log. Upon inspecting the entry added, developers will notice that the entry for the Product column is "Unknown." This is the default behavior of the SPDiagnosticsService class. To add custom product names developers can implement their own diagnostics service using the SPDiagnosticsServiceBase class.

Unfortunately the ability to interact with a custom SPDiagnosticsService is available only in fully trusted farm solutions and not within sandbox solutions. To write to the ULS logs using the SPDiagnosticsService class from the sandbox, developers can create a fully trusted proxy that the sandbox solutions can call into. The next two sections demonstrate how to write to the SharePoint ULS logs.

Creating a Custom Logging Service
Creating a custom logging service in a custom application involves creating a new implementation of the SPDiagnosticsServiceBase class, overwriting a single method, and providing a few ways to write to the log files. Add a new class to a non-sandbox solution and have the class inherit from the SPDiagnosticsServiceBase class:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.SharePoint.Administration;

namespace MSDN.SharePoint.Samples.WriteToUls \\{

  public class LoggingService : SPDiagnosticsServiceBase \\{

    private LoggingService() :

      base("CPT Logging Service", SPFarm.Local) \\{ \\}
  \\}
\\}

Next, implement the ProvideAreas() method that will register the product name and logging categories available:

public const string CPT_INFO = " CPT Info";

public const string CPT_ERROR = " CPT Error";

private static string PRODUCT_DIAGNOSTIC_NAME = " CPT Sample";

protected override IEnumerable<SPDiagnosticsArea> ProvideAreas() \\{

  List<SPDiagnosticsArea> areas = new List<SPDiagnosticsArea>\\{

    new SPDiagnosticsArea(PRODUCT_DIAGNOSTIC_NAME, new List<SPDiagnosticsCategory>\\{

      new SPDiagnosticsCategory(CPT_INFO, TraceSeverity.Verbose, EventSeverity.Information),

      new SPDiagnosticsCategory(CPT_ERROR, TraceSeverity.Unexpected, EventSeverity.Warning),
    \\})
  \\};

   return areas;
\\}


The last step is to make the custom logging service easy to use by adding a few static methods that can be called from custom components such as Web Parts:

private static LoggingService _current;

public static LoggingService Current \\{

  get \\{

    if (_current == null)

      _current = new LoggingService();

    return _current;
  \\}
\\}
public static void LogMessage(string categoryName, string message) \\{

  SPDiagnosticsCategory category = LoggingService.Current.Areas\\[PRODUCT_DIAGNOSTIC_NAME\\].Categories\\[categoryName\\];

  LoggingService.Current.WriteTrace(0, category, TraceSeverity.Verbose, message);
\\}

 public static void LogError(string categoryName, string message) \\{

  SPDiagnosticsCategory category = LoggingService.Current.Areas\\[PRODUCT_DIAGNOSTIC_NAME\\].Categories\\[categoryName\\];

  LoggingService.Current.WriteTrace(0, category, TraceSeverity.Unexpected, message);
\\}


Test the logger by adding a custom component to the project, such as a Web Part, and call the logging service:

public class WriteToUlsFarmSolutionWebPart : WebPart \\{

  protected override void CreateChildControls() \\{

    Button logInfoButton = new Button \\{ Text = "log info entry" \\};

    logInfoButton.Click += (sender, e) =>

      LoggingService.LogMessage(LoggingService.CPT_INFO, "Sample info message.");

    this.Controls.Add(logInfoButton);

     Button logErrorButton = new Button \\{ Text = "log error entry" \\};

    logErrorButton.Click += (sender, e) =>

      LoggingService.LogMessage(LoggingService.CPT_ERROR, "Sample error message.");

    this.Controls.Add(logErrorButton);
  \\}
\\}


When the above code is run by clicking one of the buttons in the Web Part, a new entry is added  using the product name "CPT Sample" and one of the new categories.