CHAPTER-4.Troubleshoot and debug web applications
Objective 4.1: Prevent and troubleshoot runtime issues
Troubleshooting performance, security, and errors
Using Performance Wizard
Profiling is the process of analyzing a running computer program.
Performance Wizard provides several different profiling methods:
- CPU sampling
- Instrumentation
- .NET memory allocation
- Resource contention data
CPU sampling gathers information every few CPU cycles about the work occurring in the computer. It acts more as an initial check, providing direction about where you need to further examine the system.
Instrumentation enables you to better understand the source of performance problems.
.NET memory allocation analyzes every object in memory from creation to garbage collection.
Resource contention data provides information on how your threads interact with each other and with the system, CPU utilization, overlapped input/output (I/O), and many other useful metrics when trying to determine why your multithreaded application does not perform properly.
Using Visual Studio Profiler
Profiler performs a complete trace of the calls that occur in an application.
Using Performance Monitor
Performance Monitor has hundreds of individual monitors, several of which are specific to ASP.NET.
System performance counters focus on application and process start and stop and running applications, whereas application performance counters watch details going on within the application such as requests, caches, and application errors.
You can schedule the time when you want performance monitoring to run.
Troubleshooting security issues
Generally, security issues tend to be related to authentication and authorization.
Implementing tracing, logging, and debugging
NLog
and log4net
are two well-known open-source logging tools.
Tracing is a technique that enables you to analyze your application while it is running. It is a built-in feature of .NET Framework.
The easiest way to configure a TraceListener is through the Web.config
file, as follows:
Error logging can also be handled automatically by using the HandleErrorAttribute
, by overriding the controller’s OnException method, or by using a custom error filter. The HandleErrorInfo
class contains details about the exception that needed to be managed as well as information about where the error occurred. Using the HandleErrorAttribute
does not pass information through a controller, which is unnecessary. You can handle errors at the controller level by overriding the controller’s OnException
method.
LISTING 4-1 An example of overriding the OnException method in a controller
protected override void OnException(ExceptionContext exceptionContext)
{
if (exceptionContext.IsChildAction)
{
//we don't want to display the error screen if it is a child action,
base.OnException(exceptionContext);
return;
}
// log the exception in your configured logger
Logger.Log(exceptionContext.Exception);
//handle when the app is not configured to use the custom error path
if (!exceptionContext.HttpContext.IsCustomErrorEnabled)
{
exceptionContext.ExceptionHandled = true;
this.View("ErrorManager").ExecuteResult(this.ControllerContext);
}
}
You can manage exceptions at a more global level through the Application_Error
method in the Global.asax
file. It is typically used for logging and tracing the thrown exception.
Enforcing conditions by using code contracts
Code contracts involve:
- Preconditions
Conditions that have to be fulfilled before a method can execute. - Invariants
Conditions that do not change during the execution of a method.
*Postconditions
Conditions that that are verified upon completion of the method.
Contracts are a way for you to codify your dependencies and enable them to be visible by the consumers of your methods.
LISTING 4-2 Parameter checking
internal Article GetArticle(int id)
{
if (id <= 0)
{
throw new ArgumentException("id");
}
// some work here
}
Using contracts to perform this check enables consumers of the methods to get some information about the expectations of the method:
internal Article GetArticle(int id)
{
System.Diagnostics.Contracts.Contract.Requires(id > 0);
// some work here
}
An invariant contract is designed to ensure that a class does not become invalid during processing, except during brief private calls for transactional work.
Enabling and configuring health monitoring
Health monitoring is a subsystem built into ASP.NET that is specifically designed to handle logging of various web events such as application lifetime events, security events, and application errors. It is part of the ASP.NET framework so it has default access to more events than most third-party logging providers. It follows the Microsoft provider framework, so it can be added to your application through configuration.
Objective 4.2: Design an exception handling strategy
Handling exceptions across multiple layers
A layer should know only about the layer it communicates with, and it should have no knowledge about layers that might be calling it.
Displaying custom error pages, creating your own HTTPHandler, and setting Web.config attributes
The Global.asax
page is one of the ways you can support custom error pages.
LISTING 4-4 Managing errors using the Application_Error method
public void Application_Error(Object sender, EventArgs e)
{
if (Server != null)
{
//Get the context
HttpContext appContext = ((MvcApplication)sender).Context;
Exception ex = Server.GetLastError().GetBaseException();
//Log the error using the logging framework
Logger.Error(ex);
//Clear the last error on the server so that custom errors are not fired
Server.ClearError();
//forward the user to the error manager controller.
IController errorController = new ErrorManagerController();
RouteData routeData = new RouteData();
routeData.Values["controller"] = "ErrorManagerController";
routeData.Values["action"] = "ServerError";
errorController.Execute(
new RequestContext(new HttpContextWrapper(appContext), routeData));
}
}
You can also set error information in the Web.config file by adding error nodes to the
You must set
in the
section of Web.config
as well.
HTTP 500 errors are generally handled through other means than configuration, such as filters or OnException handlers.
Handle first chance exceptions
First chance exceptions are exceptions before they have been handled by an error handler. Every error that occurs in an application begins the error-handling process as a first chance exception.
LISTING 4-5 Capturing first chance exceptions in code
protected void Application_Start()
{
AppDomain.CurrentDomain.FirstChanceException +=
CurrentDomain_FirstChanceException;
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
protected void CurrentDomain_FirstChanceException(object sender,
System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
{
if (e.Exception is NotImplementedException)
{
// do something special when the functionality is not implemented
}
}
Objective 4.3: Test a web application
Creating and running unit tests
Unit test your code
Creating mocks
A shim is a small piece of code that intercepts a call to an assembly and has it return an object you created, or mocked.
A stub is code that replaces a working class with a limited subset of a class you mocked up.
Shims are generally used to provide mocks from assemblies outside of your solution, whereas stubs are used to create mocks of classes within your solution.
Create and run web tests
Types of load tests
There are three different approaches you can take when running load tests: constant, step, and goal-based.
Constant load tests enable you to set a constant number of users. The testing process uses the same users through the entire test run.
The step load test steadily adds users to the testing process. There are four values you
need to consider when using a stepped performance testing approach:
- Initial user count
The number of users that start as soon as the testing process starts - Maximum user count
The maximum number of users to be used in the process - Step duration
The value, in seconds, of the time between the start of each new group of users - Step user count
The number of users to be added after the expiration of each duration
Goal-based load test uses the user count as a way of getting to other goals, including percent of CPU usage and percent of memory usage.
Test planning
Four primary types of test approaches:
- Smoke
- Stress
- Performance
- Capacity planning
Objective 4.4: Debug a Windows Azure application
Collecting diagnostic information
The customized diagnostic tools are bundled in the Microsoft.WindowsAzure.Diagnostics
namespace, which is contained in the Windows Azure Software Development Kit (SDK).
LISTING 4-11 Adding diagnostics to the ServiceDefinition.csdef file
A sample of Windows Azure diagnostics configuration code in the Diagnostics.wadcfg
file
There are two ways to transfer this information to a Windows Azure Storage Account: ondemand and scheduled. The on-demand approach requires that code within your application, within the role, or from an external application requests the transfer. The scheduled transfer is set up during the configuration of the log directory.
Debugging a Windows Azure application
There are two options for debugging an application in Windows Azure: IntelliTrace and RDP.
IntelliTrace traces through the mechanics of a running application.