Debug method One: Read the Log Files
Even though sometimes what Sitecore logs out is crap it is still the best starting point, you will start to quickly get a sixth sense of what the most likely causes of issues are. There can be a lot of red herrings so it is important to ensure you address any application code errors like Null Reference Exceptions or configuration issues so when you do need to sort out that curly issue you are not unnecessary distracted.
Note Sitecore Log Analyzer does not look like it has been updated for recent versions of Sitecore.
Debug method Two: Read the Code
One of the untold rules of being a Sitecore Developer is "If in doubt, decompile the Sitecore Assemblies to dispel that doubt".
Please note that decompiling Sitecore assemblies may not be permitted, however the only obfuscated assembly is Sitecore.Nexus.dll, hence the above rule. If Sitecore where to turn around and obfuscate all their assemblies you would lose the most powerful debugging tool in your Sitecore Toolbelt.
When you are looking at an error from the Sitecore Logs more than likely there will be a stacktrace, this is the best place to start to narrow your search.
12064 21:01:02 ERROR Exception during Layout Service RenderItem (configuration: jss, item: /) Exception: System.Xml.XPath.XPathException Message: 'types/Foundation.Layout.Pipelines.GetLayoutServiceContext.SiteSettings, Foundation.Layout' has an invalid token. Source: System.Xml at MS.Internal.Xml.XPath.XPathParser.ParseXPathExpresion(String xpathExpresion) at System.Xml.XPath.XPathExpression.Compile(String xpath, IXmlNamespaceResolver nsResolver) at System.Xml.XPath.XPathNavigator.Select(String xpath) at System.Xml.XmlNode.SelectNodes(String xpath) at Sitecore.Pipelines.CorePipelineFactory.GetObjectNode(String objectName, XmlNode groupNode) at Sitecore.Pipelines.CorePipelineFactory.GetObject(String groupName, String pipelineName, String objectName, XmlNode groupNode) at Sitecore.Pipelines.CorePipelineFactory.GetProcessorObject(XmlNode processorNode) at Sitecore.Pipelines.CoreProcessor.GetMethod(Object parameters) at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists) at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain) at Sitecore.LayoutService.ItemRendering.Pipelines.GetLayoutServiceContext.GetLayoutServiceContextPipeline.GetData(GetLayoutServiceContextArgs args) at Sitecore.LayoutService.ItemRendering.LayoutService.Render(Item item, IRenderingConfiguration renderingConfiguration, RenderOptions renderOptions) at Sitecore.LayoutService.Mvc.Controllers.LayoutServiceController.RenderItem(String configuration, String item)
Here is an example of an error that I am debugging as I wright this blog post.
Debug method Three: Debug Sitecore Code
Note: The steps outlined may vary slightly depending on the version of visual studio.
You can step through Sitecore code, cool right, but how the hell do you do it? Well there are two methods, free with dotPeek and not free with Resharper.
Before we get into the specifics for the two methods there is a few things we need to set up:
- Disable "Just My Code"
2. Instruct the runtime not to JIT Optimise the assembly.
You don't strictly need this step but when the debugger hits a breakpoint the experience will not be optimal. A lot of the variables and members will have been optimised away and you won't be able to inspect them.
When debugging a JIT optimised assembly and you try to inspect variables you may see a message like
Cannot obtain value of the local variable or argument because it is not available at this instruction pointer, possibly because it has been optimized away.
To instruct the JIT not to optimise the assembly you need to locate the dll file that is loaded. To find it quickly you first need to attach the debugger to the Sitecore w3wp process.
Once attached you can open the Modules Window
Search for the Assembly you want to step into
We need to drop a file at the path directory you can copy the Directory Path
Create a file called
<AssemblyName>.ini in this directory with the contents of:
[.NET Framework Debugging Control] GenerateTrackingInfo=1 AllowOptimize=0
ProTip, get a friend like Alen, thanks for pointing me at this man. https://alenpelin.com/
Finally Restart IIS.
Now we are ready to generate symbol(PDB) files to allow us to step through the Sitecore code.
Program database (PDB) is a proprietary file format (developed by Microsoft) for storing debugging information about a program (or, commonly, program modules such as a DLL or EXE). PDB files commonly have a .pdb extension. A PDB file is typically created from source files during compilation.
Pro Tip: If you come back and things are still optimised, make sure the same file is in use by the framework, you may need to repeat the above steps.
dotPeek has a wonderfully useful feature for helping you to debug issues in assemblies you don't have the source code or a PDB for, the Symbol Server.
I find it best to navigate to the bin directory of the Sitecore site you are debugging, and select the assemblies you want to de-compile and server up PDB files for.
Jump over to visual studio, you will need to ensure "Just My Code" has been disabled (see above).
Navigate to the Symbols setting under debugging in the Visual Studio Options
Finaly attach to the w3p process of the SItecore instance you are wanting to debug.
Generate PDB one by one
You can also generate PDB file on an needed basis if you have ReSharper installed.
First find the assembly you want to debug via the Assembly Explore, I like to use the process explorer
Find the assembly in the list and right click it, choosing "Enable Debugging"
Specific Problems and Solutions
Debugging startup logic:
This is easier than you think:
- Set a breakpoint in the start up code.
- Deploy your project to the Sitecore web root and start the application.
- Hit the site in your browser.
- Attach Visual Studio to
- Change a config file, I like to republish one.
The app pool will restart but your debugger will still be attached and BAM your break point in the startup code will be hit.
You can also restart the web site in IIS, but I find the above quicker.
Assembly Binding issues:
When working with Sitecore you may end up with the Yellow Screen of Death complaining that the ASP.NET framework could not load assembly xxx or one of its dependencies. When I first encountered this issue I found the error very unhelpful but is, for the most part, very easy to resolve.
Additional information: Could not load file or assembly 'xxx, Version=x.x.x, Culture=neutral, PublicKeyToken=xxx' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference.
Sitecore uses assembly binding redirects that are defined in the web.config
<runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="Microsoft.Extensions.Configuration" publicKeyToken="adb9793829ddae60" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-22.214.171.124" newVersion="126.96.36.199" /> </dependentAssembly> </assemblyBinding> </runtime>
Sometimes when you install NuGet packages into your application project a different version of an assembly, that Sitecore ships with, may be deployed into the bin folder of your website, and this version could be outside the range of the bindingRedirect.
The error that is surfaced to you does not give much away to what this might be as the issue could be in one of the dependencies of
xxx, tracking that down by checking version numbers manually on all your references in your solution can be very time consuming .
The quick solution to this problem is to read the Fusion logs.
Fusion is responsible for finding the different assemblies for an application to execute and pass them onto the loader. For ASP.NET applications I believe that it looks in the GAC and the application bin folder.
By default Fusion will not log this resolution to disk, but you can enable this with a simple powershell script.
Set-ItemProperty -Path HKLM:\Software\Microsoft\Fusion -Name ForceLog -Value 1 -Type DWord Set-ItemProperty -Path HKLM:\Software\Microsoft\Fusion -Name LogFailures -Value 1 -Type DWord Set-ItemProperty -Path HKLM:\Software\Microsoft\Fusion -Name LogResourceBinds -Value 1 -Type DWord Set-ItemProperty -Path HKLM:\Software\Microsoft\Fusion -Name LogPath -Value 'C:\FusionLog\' -Type String mkdir 'C:\FusionLog\'
Like Alchemy, you have to pay a toll for this privilege, that toll is increase disk space usage and slow execution of your application, so it is important to turn this off once you are done.
Remove-ItemProperty -Path HKLM:\Software\Microsoft\Fusion -Name ForceLog Remove-ItemProperty -Path HKLM:\Software\Microsoft\Fusion -Name LogFailures Remove-ItemProperty -Path HKLM:\Software\Microsoft\Fusion -Name LogResourceBinds Remove-ItemProperty -Path HKLM:\Software\Microsoft\Fusion -Name LogPath
However we live in the future and there is a better way to achieve this now, Fusion++ allows you to record a session for when you need to diagnose an issue like this, and once you are done you end the session, no more forgetting to disable the logging and wonder why your application is so slow now and why you have no disk space left.
Once you have identified the the assembly that is causing the issue my suggestion is to check if you are referencing that version in your application project, and if is it the version fusion is trying to find. If there is a reference to a different version, that best thing to do is change the version you are referencing.
As a general rule of thumb try to use the version of an assembly that your version of Sitecore ships with. Playing around with binding redirects can get out of hand quickly and has caused many headaches for me in the past, if you have no other option however, make sure you add or change these with an config transform to isolate the change from the original web.config.