Debugging the Solution and Adding Logging Capabilities
Applies to: Duet Enterprise for Microsoft SharePoint and SAP Server 2.0 | Office 2010 | SharePoint Server 2010
In this article
Configuring a Web Application for Debugging
Debugging a Solution in Visual Studio
Using Custom Error Pages in a Deployed Solution
Implementing Custom Logging
Summary
There are several ways to debug SharePoint applications and capture information about operations within a given SharePoint deployment to help in the development and administration of a solution. This article describes preparations and requirements for debugging SharePoint applications and considers several debugging and logging mechanisms that can be implemented for the Duet Sales Order Management solution.
Configuring a Web Application for Debugging
By default, web applications in SharePoint 2010 are configured such that debugging is disabled. When an error occurs in your application, the custom ASP.NET error page is displayed, which doesn't include stack trace information and may not be as helpful to you in tracking down the source of the error. As described in Debugging SharePoint Solutions, you can enable debugging for a web application by modifying the web.config file associated with the web application. The location of the web.config file on your server depends on how your web application was set up. You can use IIS Manager to locate the web.config file for a given application.
To locate web.config
Open IIS Manager. For information about opening IIS Manager, see Open IIS Manager (IIS 7).
In the Connections pane, expand the server node and then expand the Sites node.
In the list of sites, select the root site for your solution. (This is the SharePoint site collection that contains the site specified in the Site URL property of your solution.)
In the Actions pane, click Explore. The root directory for your web application is opened in Windows Explorer. This directory contains the web.config file associated with your web application.
The web.config file is an XML file that you can edit in any text editor, such as Notepad.exe. To enable debugging in your web application, the following changes to the web.config file are required:
The CallStack attribute of the SafeMode element must be set to "true".
The mode attribute of the customErrors element in the system.web section must be set to "Off".
The debug attribute of the compilation element in the system.web section must be set to "true".
(For more information about editing web.config files, see Working with Web.config Files.)
Now, even without explicit exception handling in your code, with debugging enabled, the ASP.NET error page that is displayed when an error is encountered is generally informative enough to help you to track down the source of the error.
Debugging a Solution in Visual Studio
In the course of developing your solution, you can take advantage of the standard debugging capabilities of Visual Studio 2010. You can add breakpoints to your code and press F5 to run the Duet Sales Order Management solution in debug mode. The solution is packaged, deployed, and activated, and the Visual Studio debugger is attached to the appropriate SharePoint process (w3wp.exe) in which your solution is executed.
Note that when the Active Deployment Configuration property for your solution is set to "Default", the solution is activated on your target SharePoint site after deployment. When a SharePoint Feature is activated by Visual Studio, the Feature activation code runs in a different process from the Visual Studio debugger, so when breakpoints are encountered in Feature event receiver code, execution will not step into the debugger. To debug Feature event receiver code with breakpoints, set the Active Deployment Configuration property of the solution to "No Activation". Then, press F5 to run the solution in debug mode and manually activate the solution on the server, as explained in the section Debugging Feature Event Receivers in Debugging SharePoint Solutions.
Using Custom Error Pages in a Deployed Solution
When your solution is deployed in a production environment, users of your solution will not be running the application in the context of a Visual Studio debugger, and debugging will probably not be enabled in the web.config file for a production web application. But you may still want to capture or display information pertaining to failure conditions. The Duet Sales Order Management solution is a simplified solution that was developed without a lot of explicit error handling. You can of course add error-handling code to the solution to manage predictable error conditions in a production environment or to explicitly invoke custom error pages to relay particular information about errors that might be encountered by users of your application (even with custom errors turned off in the web.config file for your web application).
For example, you can add explicit error handling to operations in the code-behind files for the Sales Order Visual Web Parts to display error pages to users that can be informative enough for troubleshooting and yet less technical than the ASP.NET debug error page.
To add error handling
In the code-behind file for the Sales Order Header Visual Web Part (SalesOrderHeaderVisualWebPartUserControl.ascx.cs), add the following directive to the beginning of the file.
using Microsoft.SharePoint.Utilities;Then, you can include the operations of a given function within the context of a try-catch statement. For example, you could modify the Page_Load event handler as follows.
protected void Page_Load(object sender, EventArgs e) { SPContext.Current.FormContext.OnSaveHandler += FormSaveHandler; try { PopulateCurrenciesDropDown(); // Prevent updating of read-only fields. if (SPContext.Current.FormContext.FormMode == SPControlMode.Edit) { ffldSalesOrderNumber.ControlMode = SPControlMode.Display; ffldNetValue.ControlMode = SPControlMode.Display; } } catch (Exception ex) { SPUtility.TransferToErrorPage(string.Format("Error loading List Form Page: {0}\r\n\r\nStack Trace:\r\n{1}", ex.Message, ex.StackTrace)); } }Save the file.
This mechanism is similar to using something like the MessageBox function in Windows desktop development. If an error is encountered in the execution of operations in the try block, an error page such as that in Figure 1 would be displayed.
Figure 1. A custom ASP.NET error page
.gif)
This technique can be used in code that is associated with pages that will be displayed to users, such as in the code-behind routines for the Visual Web Parts in the Duet Sales Order Management solution. But it may be necessary to capture information about operations that are not directly related to visual components, such as Feature activation and deactivation. SharePoint provides extensible logging capabilities and you can take advantage of these capabilities to capture information about any number of operations in your solution and write that information to files on the SharePoint Server, as described in the next section.
Implementing Custom Logging
SharePoint uses a mechanism called the Unified Logging Service (ULS) to write logging information to files stored on the file system of a server (in %Program Files%\Common Files\Microsoft Shared\Web Server Extensions\14\LOGS in a standard SharePoint installation). ULS log files are text files that can be viewed with any text editor (such as Notepad.exe), but to simplify the analysis of these files, you can use a utility such as the SharePoint ULS Viewer to filter and search the information in these logs and to monitor events and traces as they are written to the files.
In SharePoint 2010, it is easier for developers to write to these logs than it was in Windows SharePoint Services 3.0. A developer can use the SPDiagnosticsService class directly to write to the SharePoint trace logs, by executing the WriteTrace or WriteEvent methods. With these methods, you can use existing areas and categories recognized by the ULS or you can specify a custom category, but the area (displayed as Product in the ULS Viewer) associated with your logging entry in that case will be designated as "Unknown". In order to create your own specific areas, you can use a custom logging service by implementing a class that derives from the abstract SPDiagnosticsServiceBase class. (Keep in mind that the SPDiagnosticsService class and the SPDiagnosticsServiceBase class are available only in SharePoint farm solutions. These classes are not accessible in the more limited SharePoint object model available to sandboxed solutions.)
For the Duet Sales Order Management solution, a simple custom logging service was developed to handle logging operations. You can implement this service in the solution with the following steps.
To implement custom logging
In Solution Explorer, select the DuetSalesOrderSolution project.
On the Project menu, click Add Class. The Add New Item dialog box appears with the class template already selected.
Specify SalesOrderDiagnosticService.cs as the name of the class file.
Click Add. The file is added to the project and opened for editing.
Replace the contents of the file with the following code.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.SharePoint.Administration; using Microsoft.SharePoint; namespace DuetSalesOrderSolution { // Categories for Duet Sales Order Management Solution logging and tracing. public enum CategoryID { None, Deployment, Activation, Configuration, Deactivation } class SalesOrderDiagnosticService : SPDiagnosticsServiceBase { private static string diagAreaName = "Sales Order Management"; public SalesOrderDiagnosticService() : base("Sales Order Diagnostic Service", SPFarm.Local) { } public SalesOrderDiagnosticService(string name, SPFarm farm) : base(name, farm) { } protected override IEnumerable<SPDiagnosticsArea> ProvideAreas() { List<SPDiagnosticsCategory> diagCategories = new List<SPDiagnosticsCategory>(); foreach (string catName in Enum.GetNames(typeof(CategoryID))) { // Keep default least levels of TraceSeverity and EventSeverity to Medium and Information. diagCategories.Add(new SPDiagnosticsCategory(catName, TraceSeverity.Medium, EventSeverity.Information)); } yield return new SPDiagnosticsArea(diagAreaName, diagCategories); } protected override bool HasAdditionalUpdateAccess() { return true; } public static SalesOrderDiagnosticService Local { get { return SPDiagnosticsServiceBase.GetLocal<SalesOrderDiagnosticService>(); } } public SPDiagnosticsCategory this[CategoryID id] { get { return Areas[diagAreaName].Categories[id.ToString()]; } } public static void LogMessage(CategoryID categoryId, string message) { SPDiagnosticsCategory category = SalesOrderDiagnosticService.Local[categoryId]; SalesOrderDiagnosticService.Local.WriteTrace(0, category, TraceSeverity.Medium, message); } public static void LogError(CategoryID categoryId, string message) { SPDiagnosticsCategory category = SalesOrderDiagnosticService.Local[categoryId]; SalesOrderDiagnosticService.Local.WriteTrace(0, category, TraceSeverity.Unexpected, message); } } }Save the file.
This is a standard implementation of a custom logging class derived from the SPDiagnosticsServiceBase class. A custom diagnostic area called "Sales Order Management" is defined for the solution and associated with a number of categories that can be used to identify specific kinds of activities to be logged within the custom area. You can add additional categories to the enumeration here, depending on the kind of information you want to capture.
Then, depending on what operations you want to log, you can call the LogMessage or LogError methods implemented here from other parts of your code. In the Duet Sales Order Management solution, for example, you may want to log information related to significant operations such as Feature activation and deactivation.
To log Feature activation and deactivation
In Solution Explorer, under the Features node, select the SalesOrderSiteFeature Feature.
Press F7 to edit the event receiver code file (SalesOrderSiteFeature.EventReceiver.cs) for the Feature.
Replace the FeatureActivated method in the file with the following updated code:
public override void FeatureActivated(SPFeatureReceiverProperties properties) { base.FeatureActivated(properties); SalesOrderDiagnosticService.LogMessage(CategoryID.Activation, string.Format("Activating feature: {0}.", properties.Definition.DisplayName)); try { SPWeb spWeb = (SPWeb)properties.Feature.Parent; // Create Currency list. CurrencyList currencyList = new CurrencyList(); currencyList.Activate(spWeb); // Enable list customizations. ListCustomizations listCustomizations = new ListCustomizations(externalListTitles); listCustomizations.Activate(spWeb); // Enable reporting. Reporting reporting = new Reporting(); reporting.Activate(spWeb); SalesOrderDiagnosticService.LogMessage(CategoryID.Activation, string.Format("Feature activation succeeded for {0}.", properties.Definition.DisplayName)); } catch (Exception exception) { SalesOrderDiagnosticService.LogError(CategoryID.Activation, string.Format("Failed to activate {0}: {1}", properties.Definition.DisplayName, exception.Message)); } }Then, replace the FeatureDeactivating method in the file with the following updated code:
public override void FeatureDeactivating(SPFeatureReceiverProperties properties) { SalesOrderDiagnosticService.LogMessage(CategoryID.Deactivation, string.Format("Deactivating feature: {0}.", properties.Definition.DisplayName)); try { SPWeb spWeb = (SPWeb)properties.Feature.Parent; Reporting reporting = new Reporting(); reporting.Deactivate(spWeb); ListCustomizations listCustomizations = new ListCustomizations(externalListTitles); listCustomizations.Deactivate(spWeb); base.FeatureDeactivating(properties); SalesOrderDiagnosticService.LogMessage(CategoryID.Deactivation, string.Format("Feature deactivation succeeded for {0}.", properties.Definition.DisplayName)); } catch (Exception exception) { SalesOrderDiagnosticService.LogError(CategoryID.Deactivation, string.Format("Failed to deactivate {0}: {1}", properties.Definition.DisplayName, exception.Message)); } }Save the file.
If you were to deploy (and activate) the solution, information from the activation and deactivation events of the Feature would be written to the trace logs, as can be seen in Figure 2, which shows a view of a SharePoint trace log filtered on Product (which corresponds to an "area" in terms of SharePoint diagnostics) in the ULS Viewer.
Figure 2. SharePoint trace logs showing solution events in ULS Viewer
.gif)
Note that the Product column here has values matching the logging area, Sales Order Management, which is defined in the custom logging class, SalesOrderDiagnosticService. The Category column displays the values associated with the custom logging area that are related to the particular activities being logged by the solution when the methods of the SalesOrderDiagnosticService class are called, such as, in this case, during Feature activation and deactivation.
Summary
Because SharePoint applications involve the interaction of many different components and operate across multiple contexts, debugging such applications can be challenging. In this topic, we looked at several ways to facilitate debugging SharePoint solutions in general and the Duet Sales Order Management solution in particular, taking advantage of mechanisms such as integrated debugging in Visual Studio and the extensible logging framework in SharePoint 2010.
In Deploying, Testing, and Troubleshooting the Solution, you can learn about deployment options for the solution and monitoring tools such as the SharePoint Developer Dashboard.
See Also
Other Resources
Debugging and Logging Capabilities in SharePoint 2010
Overview of Unified Logging System (ULS) Logging