Introduction To Debugging and Logging in Sharepoint 2010
Introduction To Debugging and Logging in Sharepoint 2010
Page 1 of 16
Download code
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 2 of 16
Microsoft added more logging and debugging capabilities to SharePoint 2010 to help developers include additional monitoring and troubleshooting to their custom applications. This article addresses the following new areas in SharePoint 2010: Correlation tokens Logging database Custom error pages Developer Dashboard
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 3 of 16
logging database could even be exposed by using Microsoft Business Connectivity Services (BCS) external content types and lists. Reading the SharePoint ULS Logs In addition to the new logging database in SharePoint 2010, developers can also analyze the raw SharePoint log files that are 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 instead of on a farm consisting of various servers. The log files are simply large fixed-width delimited text files that can be read and searched by using any text editor. In addition to searching raw text files, quite a few developers have published various utilities that 5 assist in reading the SharePoint log files. A quick search for SharePoint ULS viewer yields numerous resources. Some of these utilities include notifications and ways to make searching and filtering for specific criteria much easier than by using a standard text editor. 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 by using the SPDiagnosticsService class, as shown in the following example.
C#
try{ ... }catch(Exceptionex){ SPDiagnosticsService.Local.WriteTrace(0,newSPDiagnosticsCategory("MSDN",TraceSe verity.Unexpected,EventSeverity.Error),TraceSeverity.Unexpected,ex.Message,ex.St ackTrace); } The previous code snippet will write the exception details to the SharePoint ULS log. Upon inspecting the entry that is 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 by using the SPDiagnosticsServiceBase class. Unfortunately, interacting with a custom SPDiagnosticsService is available only in fully trusted farm solutions and not within sandboxed solutions. To write to the ULS logs by using the SPDiagnosticsService class from the sandbox, developers can create a full trust proxy that the sandboxed solutions can call into. The next two sections describe how to write to the SharePoint ULS logs. In addition to writing a custom SPDiagnosticsService class, the Microsoft patterns & practices group has released the Developing Applications for SharePoint 20106 guidance package, which includes a SharePoint logger. Developers should consider all these approaches when building custom applications. 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.
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 4 of 16
The following example shows how to add a new class to a non-sandboxed solution and have the class inherit from the SPDiagnosticsServiceBase class.
C#
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingMicrosoft.SharePoint.Administration; namespaceMSDN.SharePoint.Samples.WriteToUls{ publicclassLoggingService:SPDiagnosticsServiceBase{ privateLoggingService(): base("MSDNLoggingService",SPFarm.Local){} } } Next, implement the ProvideAreas method that will register the product name and logging categories available, as shown in the following example.
C#
publicconststringMSDN_INFO="MSDNInfo"; publicconststringMSDN_ERROR="MSDNError"; privatestaticstringPRODUCT_DIAGNOSTIC_NAME="MSDNSample"; protectedoverrideIEnumerable<SPDiagnosticsArea>ProvideAreas(){ List<SPDiagnosticsArea>areas=newList<SPDiagnosticsArea>{ newSPDiagnosticsArea(PRODUCT_DIAGNOSTIC_NAME,newList<SPDiagnosticsCategory>{ newSPDiagnosticsCategory(MSDN_INFO,TraceSeverity.Verbose,EventSeverity.Info rmation), newSPDiagnosticsCategory(MSDN_ERROR,TraceSeverity.Unexpected,EventSeverity. Warning), }) }; returnareas; } The following example shows the last step, which 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.
C#
privatestaticLoggingService_current; publicstaticLoggingServiceCurrent{ get{ if(_current==null) _current=newLoggingService(); return_current; } } publicstaticvoidLogMessage(stringcategoryName,stringmessage){
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 5 of 16
SPDiagnosticsCategorycategory= LoggingService.Current.Areas[PRODUCT_DIAGNOSTIC_NAME].Categories[categoryName]; LoggingService.Current.WriteTrace(0,category,TraceSeverity.Verbose,message); } publicstaticvoidLogError(stringcategoryName,stringmessage){ SPDiagnosticsCategorycategory= 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, as shown in the following example.
C#
publicclassWriteToUlsFarmSolutionWebPart:WebPart{ protectedoverridevoidCreateChildControls(){ ButtonlogInfoButton=newButton{Text="loginfoentry"}; logInfoButton.Click+=(sender,e)=> LoggingService.LogMessage(LoggingService.MSDN_INFO,"Sampleinfomessage."); this.Controls.Add(logInfoButton); ButtonlogErrorButton=newButton{Text="logerrorentry"}; logErrorButton.Click+=(sender,e)=> LoggingService.LogMessage(LoggingService.MSDN_ERROR,"Sampleerrormessage."); this.Controls.Add(logErrorButton); } } When the previous code is run by clicking one of the buttons in the Web Part, a new entry is added to the SharePoint log file by using the product name MSDN Sample and one of the new categories. Writing to the ULS Logs from Sandboxed Solutions Unfortunately, writing to the ULS logs directly is not possible from within a sandboxed solution. This is because the sandboxed solution does not have access to the logging service from the isolated user code service. However, developers can create a full trust proxy that is deployed as a farm solution, and then any sandboxed solution could take advantage of this full trust proxy. For a detailed walkthrough about creating a full trust proxy, see How To: Create and Register a Sandbox Proxy7. Create a farm solution by using the SharePoint development tools in Microsoft Visual Studio 2010, and create the same SPDiagnosticsServiceBase logging class that was created previously. The next step is to create the proxy that will call the logging service. Add another class to the project that will take the proxy operation arguments, as shown in the following example.
C#
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq;
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 6 of 16
usingSystem.Text; usingMicrosoft.SharePoint.UserCode; namespaceMSDN.SharePoint.Samples.WriteToUls{ [Serializable] publicclassLoggingMessageProxyArguments:SPProxyOperationArgs{ publicstringMessageCategoryName{get;set;} publicstringMessageContents{get;set;} publicstaticstringLoggingProxyOperationTypeName{ get{ return"LoggingProxy.LoggingMessageProxyOperation"; } } publicstaticstringLoggingProxyAssemblyName{ get{ return"MSDN.SharePoint.Samples.WriteToUlsSandboxSolution.Proxy,Version=1.0. 0.0,Culture=neutral,PublicKeyToken=[INSERTPROJECT'SPUBLICKEYTOKENHERE]"; } } } } Next, add another class to the project that performs the fully trusted operation, as shown in the following example.
C#
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingMicrosoft.SharePoint.UserCode; namespaceMSDN.SharePoint.Samples.WriteToUls{ publicclassLoggingMessageProxyOperation:SPProxyOperation{ publicoverrideobjectExecute(SPProxyOperationArgsargs){ LoggingMessageProxyArgumentsproxyArguments=argsasLoggingMessageProxyArgum ents; stringloggingCategory; switch(proxyArguments.MessageCategoryName.ToLower()){ caseLoggingService.MSDN_ERROR: loggingCategory=LoggingService.MSDN_ERROR; break; default: loggingCategory=LoggingService.MSDN_INFO; break; } //Dofulltrustaction. LoggingService.LogMessage(loggingCategory,proxyArguments.MessageContents); } }
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 7 of 16
} With the proxy's arguments and operation created, the last step is to register it with the user code service in the farm. This can be done by using the activated event in a farm-scoped Feature, as shown in the following example.
C#
publicclassLoggingMessageProxyInstallerEventReceiver:SPFeatureReceiver{ publicoverridevoidFeatureActivated(SPFeatureReceiverPropertiesproperties){ //Getproxyoperation. LoggingMessageProxyOperationproxyOperation=newSPProxyOperationType( LoggingMessageProxyArguments.LoggingProxyAssemblyNam e, LoggingMessageProxyArguments.LoggingProxyOperationTy peName); SPUserCodeServiceucService=SPUserCodeService.Local; ucService.ProxyOperationTypes.Add(proxyOperation); ucService.Update(); } publicoverridevoidFeatureDeactivating(SPFeatureReceiverPropertiesproperties){ //Getproxyoperation. LoggingMessageProxyOperationproxyOperation=newSPProxyOperationType( LoggingMessageProxyArguments.LoggingProxyAssemblyNam e, LoggingMessageProxyArguments.LoggingProxyOperationTy peName); //Addproxytousercodeservice. SPUserCodeServiceucService=SPUserCodeService.Local; ucService.ProxyOperationTypes.Remove(proxyOperation); ucService.Update(); } } After this project is deployed and the included farm-scoped Feature is activated, the proxy can be called from within a sandboxed solution. The following code shows how a Web Part that is deployed in a sandboxed solution can use the full trust proxy and write to the SharePoint 2010 ULS logs.
C#
publicclassWriteToUlsSandboxSolutionWebPart:WebPart{ protectedoverridevoidCreateChildControls(){ LoggingMessageProxyArgumentsloggingProxyArgs=newLoggingMessageProxyArguments (); ButtonlogInfoButton=newButton{Text="loginfoentry"}; logInfoButton.Click+=(sender,e)=>{ loggingProxyArgs.MessageCategoryName=LoggingService.MSDN_INFO; loggingProxyArgs.MessageContents="Sampleinfomessage"; varresult1=SPUtility.ExecuteRegisteredProxyOperation(
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 8 of 16
LoggingMessageProxyArguments.LoggingProxyAssemblyName, LoggingMessageProxyArguments.LoggingProxyOperationTypeName, loggingProxyArgs); }; this.Controls.Add(logInfoButton); ButtonlogErrorButton=newButton{Text="logerrorentry"}; logInfoButton.Click+=(sender,e)=>{ loggingProxyArgs.MessageCategoryName=LoggingService.MSDN_ERROR; loggingProxyArgs.MessageContents="Sampleinfomessage"; varresult2=SPUtility.ExecuteRegisteredProxyOperation( LoggingMessageProxyArguments.LoggingProxyAssemblyName, LoggingMessageProxyArguments.LoggingProxyOperationTypeName, loggingProxyArgs); }; this.Controls.Add(logErrorButton); } } For guidelines about writing the logging event and trace events to the ULS logs, see Trace and 8 Event Log Severity Levels in the SharePoint 2010 SDK
C#
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 9 of 16
publicoverridevoidFeatureActivated(SPFeatureReceiverPropertiesproperties){ SPWebApplicationWebApp=(SPWebApplication)properties.Feature.Parent; foreach(ModificationEntrymodEntryinEntries){ WebApp.WebConfigModifications.Add( CreateModification(modEntry) ); } WebApp.WebService.ApplyWebConfigModifications(); } publicoverridevoidFeatureDeactivating(SPFeatureReceiverPropertiesproperties){ SPWebApplicationWebApp=(SPWebApplication)properties.Feature.Parent; foreach(ModificationEntrymodEntryinEntries){ WebApp.WebConfigModifications.Remove( CreateModification(modEntry) ); } WebApp.WebService.ApplyWebConfigModifications(); } privatestructModificationEntry{ publicstringName; publicstringXPath; publicstringValue; publicSPWebConfigModification.SPWebConfigModificationTypeModType; //Parameterizedcontructor.publicModificationEntry( stringName,stringXPath,stringValue, SPWebConfigModification.SPWebConfigModificationTypeModType){ //Intializestructureinstances.this.Name=Name; this.XPath=XPath; this.Value=Value; this.ModType=ModType; } } privateModificationEntry[]Entries={ newModificationEntry( "mode","configuration/system.web/customErrors","Off", SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute) }; privateSPWebConfigModificationCreateModification(ModificationEntrymodEntry){ //CreateandreturnSPWebConfigModificationobject. SPWebConfigModificationmodification; modification=newSPWebConfigModification(modEntry.Name,modEntry.XPath); modification.Owner="MSDN.SharePointDebugger"; modification.Sequence=0; modification.Type=modEntry.ModType; modification.Value=modEntry.Value; returnmodification; }
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 10 of 16
Using the Layouts web.config File You should keep in mind that folders in a web application inherit the web.config file settings from their parent folders, unless they are overwritten by a local web.config file. Specific to SharePoint 2010, the http://.../_layouts folder contains its own web.config file that has the custom errors turned on. Therefore, when creating custom application pages or troubleshooting the SharePoint 2010 application pages, developers may want to either modify the web.config located in the {SharePoint Root}\TEMPLATE\LAYOUTS folder, or add a custom web.config that turns off custom errors in the folder containing the custom application pages.
C#
publicclassCustomErrorPagesEventReceiver:SPFeatureReceiver{ publicoverridevoidFeatureActivated(SPFeatureReceiverPropertiesproperties) { SPWebApplicationwebApp=properties.Feature.ParentasSPWebApplication; if(webApp!=null){ //SetAccessDenied.if(!webApp.UpdateMappedPage(SPWebApplication.SPCustomPag e.AccessDenied, "/_layouts/CustomErrorPages/AccessDenied.aspx")){ thrownewApplicationException("Failedsettingnewaccessdeniedpagemapping ."); } //SetSignout.if(!webApp.UpdateMappedPage(SPWebApplication.SPCustomPage.Sign out, "/_layouts/CustomErrorPages/Signout.aspx")){ thrownewApplicationException("Failedsettingnewsignoutpagemapping."); } //SetFileNotFound. webApp.FileNotFoundPage="/_layouts/1033/FileNotFound.htm"; webApp.Update(true); } }
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 11 of 16
The Developer Dashboard can be very helpful in troubleshooting problematic components on a page, because a single SharePoint page can contain multiple custom components. When a page contains multiple custom components and the performance of a page is slower than desired, it can be hard to isolate which component is the root cause for the slow performance, other than by using
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 12 of 16
a simple process of elimination. This is exactly the scenario the Developer Dashboard is designed to assist with. The Developer Dashboard includes a few different categories of information. It provides details on how long it took treads to process, details on the quantity and execution duration of SQL Server database calls and Windows Communication Foundation (WCF) service calls, details on the URL, and the current user, and more. Configuring the Developer Dashboard By default, the Developer Dashboard is not turned on. Enabling the Developer Dashboard can be done either through the API or by using a custom Windows PowerShell script, as shown in the following example.
Windows PowerShell
$contentService=[Microsoft.SharePoint.Administration.SPWebService]::ContentService $dashboardSetting=$contentService.DeveloperDashboardSettings $dashboardSetting.DisplayLevel=[Microsoft.SharePoint.Administration.SPDeveloperDas hboardLevel]::On $dashboardSetting.Update() There are two different display levels available on the Developer Dashboard. The SPDeveloperDashboardLevel.On enumeration option enables the dashboard on all pages in a farm. Setting it to SPDeveloperDashboardLevel.OnDemand leaves it disabled, but adds a performance counter-like image to the top-right corner of the page. When this image is clicked, a postback is triggered that adds the Developer Dashboard to the page. For information about the Developer Dashboard, see Using the Developer Dashboard SharePoint 2010 SDK.
10
in the
Although the Developer Dashboard includes a considerable amount of information for SharePoint processes, developers can also add their own logging in custom components that appear in the dashboard. Writing to the Developer Dashboard Writing to the Developer Dashboard can be done only directly from farm solutions; sandboxed solutions cannot write to the Developer Dashboard because they are isolated in the user code process. To write to the Developer Dashboard, a custom component should be wrapped in a SPMonitoredScope object. The following code shows how to write to the Developer Dashboard and how to create nested scopes.
C#
publicclassMonitoredWebPart:WebPart{ protectedoverridevoidCreateChildControls(){ using(SPMonitoredScopescope=newSPMonitoredScope("MonitoredWebPart.CreateChi ldControls")){ this.Controls.Add( newLiteralControl("Turnonthedeveloperdashboardtoseeentriesloggedby thisWebPart.");
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 13 of 16
//TriggeraSPRequest&DBroundtrip. TriggerSpDatabaseRequest(); TriggerSPRequestObject(); //Simulateadelay. TriggerSimulatedDelay(); ); } } privatevoidTriggerSpDatabaseRequest(){ using(SPMonitoredScopescope=newSPMonitoredScope("MonitoredWebPart.TriggerSp DatabaseRequest")){ SPListsomeList=SPContext.Current.Web.Lists[0]; intitemsinList=someList.Items.Count(); } } privatevoidTriggerSPRequestObject(){ using(SPMonitoredScopescope=newSPMonitoredScope("MonitoredWebPart.TriggerSP RequestObject")){ using(SPSitesiteCollection=newSPSite(SPContext.Current.Site.Id)){ stringsiteCollectionUrl=siteCollection.RootWeb.Url; } } } privatevoidTriggerSimulatedDelay(){ using(SPMonitoredScopescope=newSPMonitoredScope("MonitoredWebPart.TriggerSi mulatedDelay")){ System.Threading.Thread.Sleep(2000); } } } Figure 2 shows the results of adding the Web Part shown in the previous code example to the page.
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 14 of 16
Adding the Developer Dashboard to Custom Master Pages The Developer Dashboard is included in all SharePoint sites that use one of the master pages included in SharePoint 2010. When creating custom master pages, developers can add the Developer Dashboard by simply adding the following server control, ideally to the bottom of the page: <SharePoint:DeveloperDashboard runat="server" /> When the Developer Dashboard is set to SPDeveloperDashboardLevel.OnDemand, it uses the dashboard launcher to display the icon and render the dashboard. Developers will also want to add the following control to custom master pages also: <SharePoint:DeveloperDashboardLauncher runat="server" /> This control contains a few public properties to change the placement, text, and image used to 11 render the control. These can be found in the DeveloperDashboardLauncher class in the SharePoint 2010 SDK.
Conclusion
Microsoft put a significant focus on the developer story in the latest release of SharePoint, Microsoft SharePoint 2010. In addition to the first-class developer tools included in Visual Studio 2010, Microsoft added considerable flexibility and capabilities for developers to add logging and debugging techniques to custom solutions built for SharePoint 2010. This article walks through some common logging and debugging techniques that are now available to developers when building SharePoint 2010 solutions.
Additional Resources
For more information, see the following resources: Logging For SharePoint Developers12
3 Configure Diagnostic Logging (SharePoint Foundation 2010) (TechNet)
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 15 of 16
Timer Job References (SharePoint Server 2010)4 (TechNet) Database Types and Descriptions (SharePoint Server 2010)
13
(TechNet)
14
Using Business Connectivity Services to Display SharePoint 2010 ULS Logs SharePoint ULS Log Viewer SharePoint Guidance
16 15
Change History
Links Table
1 2 3 4 5 6 7 8 9
http://msdn.microsoft.com/enus/library/microsoft.sharepoint.administration.spwebapplication.spcustompage.aspx
10 11
http://msdn.microsoft.com/en-us/library/ff512745.aspx
12 13 14
http://www.shillier.com/archive/2010/08/05/Using-Business-Connectivity-Services-to-DisplaySharePoint-2010-ULS-Logs.aspx
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011
Page 16 of 16
15 16 17
Community Content
2011 Microsoft. All rights reserved.
http://msdn.microsoft.com/en-us/library/gg512103(d=printer).aspx
11/23/2011