Appearance
Are you an LLM? You can read better optimized documentation at /troubleshooting/debugging.md for this page in Markdown format
Debugging
This document provides a comprehensive guide for debugging the TheExampleApp application. It covers environment setup, tooling, and strategies for identifying and resolving issues in different parts of the stack, from the backend C# code to the Contentful integration.
Development environment setup
Effective debugging begins with a correctly configured development environment. The application's behavior, particularly its error handling, is controlled by the ASPNETCORE_ENVIRONMENT environment variable.
The primary configuration for local development is located in TheExampleApp/Properties/launchSettings.json. This file defines launch profiles for both IIS Express and the Kestrel web server (via the "Project" command).
json:TheExampleApp/Properties/launchSettings.json
{
// ...
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "http://localhost:59989/",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"TheExampleApp": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:3000/"
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Key Takeaways:
ASPNETCORE_ENVIRONMENT: "Development": This is the most critical setting for debugging. When this variable is set toDevelopment, the application enables developer-friendly features, most notably the Developer Exception Page.- Enabling the Setting: When you run the application from Visual Studio (using either profile) or via the
dotnet runcommand, this environment variable is automatically applied, ensuring you are in debugging mode. - Impact on Startup: The value of this variable is consumed in
Startup.csto conditionally add middleware to the request pipeline.
For more details on how environment variables control the application's configuration, see the Environment Variables documentation.
Breakpoint debugging
Standard breakpoint debugging is fully supported and is the primary method for inspecting application state at runtime. You can set breakpoints in Visual Studio or VS Code.
Where to Set Breakpoints:
- C# Code (
.csfiles): Breakpoints can be set in any C# file, including controllers, services, middleware, and theStartup.csconfiguration class. - Razor Views (
.cshtmlfiles): You can set breakpoints directly within Razor files to debug server-side rendering logic.
Example Scenario: Debugging Localization
The application uses a complex CustomRequestCultureProvider to determine the user's language. To debug this logic, you can set a breakpoint within the lambda function in Startup.cs.
csharp:TheExampleApp/Startup.cs
// ... in Configure() method
app.UseRequestLocalization(new RequestLocalizationOptions
{
RequestCultureProviders = new List<IRequestCultureProvider>
{
new CustomRequestCultureProvider(async (s) => {
// <<< SET BREAKPOINT HERE to inspect incoming requests and session state.
if (s.Request.Query.ContainsKey(LOCALE_KEY))
{
// ...
}
return null;
}),
// ...
}
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
When a request hits the server, execution will pause at this breakpoint, allowing you to inspect the HttpContext (s), query parameters, session values, and step through the culture selection logic.
Logging
The application is configured with standard ASP.NET Core logging. The WebHost.CreateDefaultBuilder(args) method in Program.cs sets up console and debug log providers by default.
To add logging to any class, inject the ILogger<T> interface via the constructor.
Example: Adding Logging to a Service
csharp
public class MyService
{
private readonly ILogger<MyService> _logger;
private readonly IContentfulClient _client;
public MyService(ILogger<MyService> logger, IContentfulClient client)
{
_logger = logger;
_client = client;
}
public async Task<Course> GetCourse(string slug)
{
_logger.LogInformation("Fetching course with slug: {Slug}", slug);
try
{
var course = await _client.GetCourseBySlug(slug);
if (course == null) {
_logger.LogWarning("Course with slug {Slug} not found.", slug);
}
return course;
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while fetching course {Slug}", slug);
throw;
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
During development, these logs will appear in the Visual Studio Debug output window or the console window if running via dotnet run. In production or containerized environments, these logs are essential for diagnostics. Log levels can be configured in appsettings.Development.json.
Developer exception page
When ASPNETCORE_ENVIRONMENT is set to Development, the application uses the Developer Exception Page middleware. This is enabled in Startup.cs:
csharp:TheExampleApp/Startup.cs
// ... in Configure() method
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Error");
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
This middleware catches any unhandled exception and renders a detailed HTML page containing:
- Stack trace
- Query parameters
- Cookies and Headers
- Routing information
In non-development environments, the else block is executed, which redirects the user to a generic error page handled by Error.cshtml. The model for this page, ErrorModel, contains specific logic to handle ContentfulException.
csharp:TheExampleApp/Pages/Error.cshtml.cs
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
// Get the last exception that occurred.
var ex = HttpContext?.Features?.Get<IExceptionHandlerFeature>()?.Error;
if(ex is ContentfulException)
{
ContentfulException = ex as ContentfulException;
// This RequestId is from Contentful and is crucial for support tickets.
RequestId = ContentfulException.RequestId;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
This ensures that even in production, a failed API call to Contentful will expose a RequestId that can be used to trace the operation on the Contentful backend. For more information on middleware, see the Middleware Configuration documentation.
Contentful API debugging
Since Contentful is the primary data source, debugging its integration is critical. The application provides a powerful mechanism for redirecting API calls to a staging environment.
This is achieved via a custom HttpClient registration and a HttpClientHandler in Startup.cs.
csharp:TheExampleApp/Startup.cs
// ... in ConfigureServices() method
if (CurrentEnvironment.IsStaging())
{
var host = Environment.GetEnvironmentVariable("StagingHost");
if (!string.IsNullOrEmpty(host))
{
services.AddSingleton((ip) =>
{
var stagingHandler = new StagingMessageHandler
{
StagingHost = host
};
return new HttpClient(stagingHandler);
});
}
}
// ...
public class StagingMessageHandler : HttpClientHandler
{
public string StagingHost { get; set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var regex = new Regex("contentful");
var req = regex.Replace(request.RequestUri.ToString(), StagingHost, 1);
request.RequestUri = new Uri(req);
return await base.SendAsync(request, cancellationToken);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
How to Use:
- Set the
ASPNETCORE_ENVIRONMENTtoStaging. - Set the
StagingHostenvironment variable to the hostname of your alternative Contentful environment (e.g.,preview.contentful.comor a mock server). - Run the application. All outgoing requests from the
ContentfulClientwill be transparently redirected to the host specified inStagingHost.
This allows you to test against draft content or a separate Contentful space without changing application code.
Browser developer tools
Although the application is primarily server-rendered using Razor, browser developer tools are still invaluable for debugging.
- Network Tab:
- Inspect the final HTML response sent from the server.
- Check the status codes of requests. A
500error indicates a server-side exception. A404might indicate a routing issue or a missing resource. - Examine request and response headers.
- Application Tab:
- Inspect cookies. The session identifier cookie (typically
.AspNetCore.Session) is stored here. - View session storage or local storage if any client-side scripts use them.
- Inspect cookies. The session identifier cookie (typically
- Console Tab:
- Check for any client-side JavaScript errors, which can occur even in a server-rendered app.
Docker debugging
The provided Dockerfile is configured for a production-like environment, which has important implications for debugging.
dockerfile:Dockerfile
FROM microsoft/dotnet:2.1-sdk AS builder
# ... build steps ...
FROM microsoft/aspnetcore:2.1
WORKDIR /app
ENV ASPNETCORE_ENVIRONMENT=Heroku
COPY --from=builder /app .
ENTRYPOINT ["dotnet", "TheExampleApp.dll"]1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
The line ENV ASPNETCORE_ENVIRONMENT=Heroku means that when the container runs, env.IsDevelopment() in Startup.cs will evaluate to false.
Consequences:
- The Developer Exception Page is disabled.
- Unhandled exceptions will be processed by
app.UseExceptionHandler("/Error"). - You will not see detailed exception information in the browser.
Debugging Strategy for Docker:
- Check Container Logs: This is the most important step. Use the
docker logscommand to view the standard output from the container. All information written viaILoggerwill appear here, including exception details and stack traces.bashdocker logs <your-container-name-or-id>1 - Interactive Debugging (Advanced): For interactive debugging inside a container, you will need to either:
- Modify the
Dockerfileto setASPNETCORE_ENVIRONMENTtoDevelopment. - Use Visual Studio's "Container Tools" which can automate attaching the debugger to the process running inside the container.
- Modify the
Session state inspection
Session state is enabled via app.UseSession() and is used for the application's complex localization logic, storing the LOCALE_KEY and FALLBACK_LOCALE_KEY.
During a debugging session with breakpoints, you can inspect the session state directly from the HttpContext object.
Example Inspection in a Razor Page or Controller:
csharp
// In a method with access to HttpContext
var currentLocale = HttpContext.Session.GetString("locale");
var fallbackLocale = HttpContext.Session.GetString("fallback-locale");
// You can inspect these variables in the debugger's Watch or Locals window.1
2
3
4
5
2
3
4
5
Because the default session provider uses a cookie for storage, you can observe the session cookie in your browser's developer tools, but its contents are protected and not human-readable. The most effective way to inspect session data is with the C# debugger.
Performance profiling
If you encounter performance issues such as slow page loads or high resource consumption, use a profiler to identify bottlenecks. Visual Studio's built-in Performance Profiler (accessible via Debug > Performance Profiler...) is a powerful tool for this.
Key Profiling Tools to Use:
- CPU Usage: Identifies "hot paths"—methods where the application spends most of its execution time. This is useful for finding inefficient algorithms or tight loops.
- .NET Async: This tool is critical for an application that makes heavy use of
async/awaitfor network calls (e.g., to Contentful). It helps visualize the duration and relationship of asynchronous tasks, making it easy to see how much time is spent waiting for the Contentful API to respond. - Memory Usage: Helps detect memory leaks or high-allocation patterns that can lead to performance degradation due to excessive garbage collection.
When profiling, pay close attention to the time spent within the Contentful.Core library's methods. If a large portion of request time is spent there, the bottleneck may be network latency or the performance of the Contentful API itself, rather than an issue in the application code.