How Can I Use OpenTelemetry in .NET to Track a Slow API Request?

Have you ever gotten a bug report stating that an API is responding slowly, but you are unable to identify the cause of the delay?

Before providing a response, the request may take some time to run application code, wait for a database query, call another API, or process data. You cannot determine which component is at fault by looking simply at the overall response time.

In a microservices application, where a single request may go through many services before receiving the final result, this becomes much more challenging.

This is a common problem in production environments. Checking whether an application process is running does not tell you whether the application can actually serve requests.

This is where health checks become useful.

Health checks allow you to monitor whether an application and its important dependencies are working as expected. You can use them to check database connectivity, verify access to required services, and provide health information to monitoring systems or container orchestration platforms.

In this article, we will learn how to add health checks to an ASP.NET Core application, check SQL Server connectivity, expose health endpoints, and configure different health checks for production use.

What Are Health Checks in ASP.NET Core?

Health checks are endpoints or checks that report the condition of an application or one of its dependencies.

ASP.NET Core includes built-in health-check services through Microsoft.Extensions.Diagnostics.HealthChecks. You can register checks using dependency injection and expose their results through HTTP endpoints.

For example, an application might need the following components to work correctly:

  • The ASP.NET Core application itself.
  • A SQL Server database.
  • A Redis cache.
  • An external service used to process payments.

A health check can help determine whether these components are available.

However, health checks do not automatically detect every application problem. You must decide which components to check and what each result should mean for your application.

Liveness vs. Readiness Health Checks

Before writing code, it is important to understand the difference between liveness and readiness.

Liveness answers the question: Is the application process healthy enough to continue running?

Readiness answers a different question: Is the application ready to receive requests?

Consider an application that has started but is still loading required configuration or cannot access a critical dependency. It might be alive but not ready to serve traffic.

FeatureLiveness CheckReadiness Check
PurposeDetermines whether the application should continue runningDetermines whether the application should receive traffic
Typical checksBasic process or application responsivenessRequired dependencies and initialization state
Common useContainer restart decisionsLoad balancer traffic decisions
Database required?Usually notDepends on application requirements

Keeping these checks separate helps prevent unnecessary restarts. For example, a temporary database outage should not automatically cause an orchestrator to restart an otherwise healthy application repeatedly.

Step 1: Register Health Checks

Create an ASP.NET Core Web API project if you do not already have one.

Open Program.cs and register the health-check services.

The important line is:

It registers the health-check services with dependency injection.

The following line exposes the health-check endpoint:

After starting the application, send a GET request to /health.

If no additional checks have been registered, the endpoint can report that the application is healthy because there are no failing checks.

This is a useful starting point, but it does not verify whether SQL Server or another required dependency is available.

Step 2: Add a SQL Server Health Check

Many applications depend on a database. If the database becomes unavailable, operations that require it may fail even though the application process continues running.

ASP.NET Core supports database health checks through provider-specific health-check packages.

For SQL Server, install the SQL Server health-check package:

Next, configure the connection string in appsettings.json.

Replace the placeholder values with your actual database configuration. Do not commit real production credentials to source control.

Now register the SQL Server health check.

The AddSqlServer() method registers a check that verifies database connectivity.

The name identifies the check, while the ready tag lets you select it for a readiness endpoint later.

The check’s exact behavior depends on the package version and configuration. Make sure the package is compatible with your target framework.

Step 3: Create Separate Liveness and Readiness Endpoints

A single health endpoint may be enough for a small application, but production systems often benefit from separate liveness and readiness endpoints.

For example, liveness should generally avoid checking remote dependencies. Otherwise, a temporary database outage could make the application appear unhealthy enough to restart.

Readiness, on the other hand, can include required dependencies that must be available before the application receives traffic.

Update Program.cs as follows:

Let’s understand the configuration.

The liveness endpoint uses:

This excludes all registered health checks. It provides a basic endpoint indicating that the application can respond to the health-check request.

The readiness endpoint uses:

This runs only the health checks tagged with ready, including the SQL Server check.

Your monitoring system can now use the endpoints for different purposes:

EndpointPurpose
/health/liveCheck whether the application responds
/health/readyCheck whether the application and required dependencies are ready

For more complex applications, liveness may need an application-specific check that detects a genuinely unrecoverable internal state. Avoid adding routine dependency checks to liveness without a clear reason.

Step 4: Return Useful Health-Check Results

By default, the health-check middleware returns a status such as Healthy, Degraded, or Unhealthy. Its default response body is simple and does not include detailed information about each check.

You can customize the response format when a monitoring system needs structured data.

For example, add a JSON response writer:

This example returns a JSON object containing the overall status and the status of each selected check.

Use the customized endpoint instead of mapping the same readiness route twice. In a complete application, replace the earlier /health/ready mapping with this version.

For production applications, avoid returning exception messages, connection strings, server names, or other sensitive details in publicly accessible health responses. Keep detailed diagnostics in secured logs and monitoring systems.

Best Practices for Production Applications

1. Keep liveness and readiness separate

Use liveness to determine whether the application can continue running. Use readiness to determine whether it should receive traffic.

This distinction helps prevent dependency failures from triggering unnecessary restarts.

2. Set appropriate timeouts

Health checks should finish within a reasonable amount of time. A database that takes several seconds to respond should not cause health endpoints to hang indefinitely.

Configure suitable timeouts and cancellation behavior for checks that may access remote services.

3. Monitor only important dependencies

Do not add every external service to every health check. Check dependencies that matter to the endpoint’s purpose.

For example, if an optional reporting service is unavailable but the main API can still serve requests, the entire application may not need to be marked unready.

4. Secure detailed health information

Health endpoints can reveal information about internal dependencies and application structure.

Keep detailed responses private or restrict access where appropriate. Public endpoints should return only the minimum information needed by the monitoring system.

5. Use health checks with monitoring tools

Health endpoints are useful when integrated with load balancers, container orchestrators, and monitoring platforms.

However, an endpoint alone does not provide historical metrics, alerting, or trend analysis. Use a monitoring system when you need to track availability over time.

Common Mistakes and Troubleshooting

ProblemPossible causeWhat to check
Health endpoint returns 404Endpoint is not mapped or the route is incorrectVerify MapHealthChecks() and the requested path
Database check failsInvalid credentials, network issues, or unavailable SQL ServerTest connectivity and verify the connection string
Liveness fails during a database outageDependency checks are included in livenessSeparate liveness from readiness
Health endpoint takes too longA dependency check is slowConfigure appropriate timeouts
Monitoring reports unhealthy statusA required dependency is failingInspect secured logs and check-level results
Sensitive details appear in responsesDetailed diagnostics are exposedRestrict access and simplify the response writer

One common mistake is treating a successful liveness check as proof that the entire application is working. It only tells you what that endpoint has been designed to verify.

Similarly, a failed readiness check does not always mean the application needs to restart. It may simply need to stop receiving traffic until a required dependency recovers.

Advantages and Disadvantages of Health Checks

Advantages

  • Help detect unavailable dependencies.
  • Allow load balancers to avoid sending traffic to unready instances.
  • Support container orchestration and automated recovery.
  • Make application availability easier to monitor.
  • Help distinguish process health from dependency readiness.

Disadvantages

  • Require careful configuration to avoid misleading results.
  • Can add load to databases and external services if checks run too frequently.
  • May cause unnecessary restarts if liveness checks depend on external services.
  • Need access controls when detailed diagnostics are exposed.

Summary

Health checks are an important part of building and operating reliable ASP.NET Core applications. They help you determine whether an application is responding and whether its required dependencies are available.

With AddHealthChecks() and MapHealthChecks(), you can create health endpoints with minimal configuration. Provider-specific checks let you verify dependencies such as SQL Server, while tags allow you to separate liveness and readiness checks.

For production applications, keep these endpoints focused, configure suitable timeouts, protect sensitive diagnostic information, and integrate them with your monitoring infrastructure.

The goal is not simply to report whether the application is running. It is to provide accurate health information that helps your infrastructure respond appropriately when something goes wrong.

Recommendation for ASP.NET 11.0 Hosting

A solid base for developing online services and applications is ASP.NET Core 11. Before creating an ASP.NET web application, you must be proficient in JavaScript, HTML, CSS, and C#. There are thousands of web hosting providers offering ASP.NET hosting on the market. However, there are relatively few web hosting providers that offer top-notch ASP.NET hosting.

ASP.NET is the best development language in Windows platform, which is released by Microsoft and widely used to build all types of dynamic Web sites and XML Web services. With this article, we’re going to help you to find the best ASP.NET Hosting solution in Europe based on reliability, features, price, performance and technical support. After we reviewed about 30+ ASP.NET hosting providers in Europe, our Best ASP.NET Hosting Award in Europe goes to HostForLIFE.eu, one of the fastest growing private companies and one of the most reliable hosting providers in Europe.

You may also like...

Popular Posts