Don’t trust a green uptime check to tell you an application is healthy. Your health probe can return 200 every ten seconds while a customer stares at a frozen checkout button for eleven seconds, gives up, and closes the tab. Uptime tells you the process is running. It says nothing about whether the process is doing its job.
Skip real observability, and you’re left debugging a slow checkout page with nothing but a green checkmark and a support queue full of angry customers. Azure Application Insights exists so that never happens to you. Instead of a single up-or-down signal, you get a place to ask why a specific request was slow, which downstream call caused it, and how many customers it touched.
What “Healthy” Actually Means to Application Insights
Application Insights is the Application Performance Management (APM) layer of Azure Monitor, an ingestion and query pipeline that watches requests, dependency calls, exceptions, and custom events as they happen. An uptime check answers one question: did something respond? Application Insights answers a different set of questions, because it captures three separate kinds of signal: traces (what happened, in order), metrics (how much and how fast), and logs (the raw diagnostic detail behind both). A checkout page can pass every uptime check while its payment dependency quietly climbs from 200 milliseconds to four seconds, and only the metrics and traces will show you that climb before support tickets do.
The Tables Behind the Portal
Every chart and graph you see in the Application Insights portal is a view over a small set of underlying tables. A requests row exists for every inbound call your app handles. A dependencies row exists for every outbound call it makes, to a database, a REST API, or a cache. An exceptions row captures anything your code throws, handled or not. A customEvents row holds whatever business-specific milestones you choose to track yourself, and a traces row holds diagnostic text you log along the way. None of this requires you to build a dashboard from scratch. It requires you to get telemetry into those tables in the first place, which is a separate problem from watching the dashboard afterward.
How Telemetry Gets From Your Code to the Portal
Getting data into those tables comes down to a choice between two approaches: auto-instrumentation, which asks nothing of your source code, or code-based instrumentation through the Azure Monitor OpenTelemetry Distro, which gives you more control at the cost of a few lines of setup.
Auto-instrumentation is the right call when you can’t or don’t want to touch application code: legacy services, containers you didn’t build, or environments where a redeploy is its own project. It injects the collection logic at runtime and hands you requests, dependencies, and metrics with zero source changes.
Code-based instrumentation is the right call when you need custom business telemetry, fine control over sampling, or distributed tracing that follows a request across several services. Microsoft’s classic Application Insights SDKs still function, but they’re in maintenance mode. Current guidance points toward the Azure Monitor OpenTelemetry Distro instead, because it puts your telemetry on the vendor-neutral OpenTelemetry standard rather than a Microsoft-only format. For an ASP.NET Core app, enabling it looks like this:
using Azure.Monitor.OpenTelemetry.AspNetCore; var builder = WebApplication.CreateBuilder(args); // Registers the Azure Monitor OpenTelemetry Distro for this application. builder.Services.AddOpenTelemetry().UseAzureMonitor(); var app = builder.Build(); app.Run();
The AddOpenTelemetry().UseAzureMonitor() call wires up automatic tracing for incoming HTTP requests, outgoing HttpClient and SQL client calls, and the standard Application Insights metrics, all without you writing a tracking call anywhere in your business logic. This guide targets Azure.Monitor.OpenTelemetry.AspNetCore version 1.6.0, the current stable release of the distro.
The Connection String Replaces the Instrumentation Key
The distro needs to know where to send what it collects, and that’s the job of the connection string, which has replaced the older instrumentation key. Microsoft recommends setting it as an environment variable in production, not in code, so you can rotate it without a redeploy:
export APPLICATIONINSIGHTS_CONNECTION_STRING="<your-connection-string>"
If you set the connection string in more than one place, code wins over the environment variable, which wins over a configuration file, a precedence order worth remembering before a leftover local value quietly overrides production.
Reading the Story Your Telemetry Is Telling
Once telemetry starts flowing, three tools turn raw table rows into something you can actually read during an incident.
Application Map and Live Metrics
The Application map draws your application’s topology by following the HTTP dependency calls your instrumented services report, so a service you thought was healthy shows up as a red node the moment its failure rate spikes. Live metrics streams request rates, dependency durations, and exception counts to the portal with about one second of latency and without writing anything to storage, which makes it the tool you open during a deployment rather than after one.

Live Metrics has one gap worth knowing about before you rely on it during an incident: it’s enabled by default for ASP.NET Core, Java, Node.js, and Python through the OpenTelemetry distro, but it’s not supported on classic ASP.NET. If your production app is still on the older framework, don’t assume the Live Metrics pane will show anything.
Performance and Failures Views
The Performance and Failures views round this out by breaking down response times and grouping failed requests by endpoint and status code, so you can go from “something is slow” to “this specific dependency call is slow” without writing a query. Both views pull from the same requests, dependencies, and exceptions tables that back the Application Map, so what you see there and what you’d get from a KQL query against those tables always agree.
Querying Telemetry With KQL When the Map Isn’t Enough
The Application Map and the Failures view answer “where” and “what.” They don’t answer “how many, over what window, compared to what baseline,” and that’s where the Kusto Query Language (KQL) takes over. KQL is a read-only, pipe-delimited query language, and every Application Insights table is queryable through it once you’re in the Log Analytics workspace.
Suppose your checkout-api service is throwing intermittent failures and you want to know which specific operation is driving them:
requests
| where timestamp > ago(15m)
| where cloud_RoleName == "checkout-api"
| summarize Total = count(), Failures = countif(success == false),
FailureRate = round(100.0 * countif(success == false) / count(), 2)
by name
| where FailureRate > 10 and Total > 5
The where FailureRate > 10 and Total > 5 clause matters as much as the calculation above it. Without a minimum execution threshold, a rarely called endpoint that failed once out of two calls reports a 50% failure rate and buries the operation that’s actually hurting real traffic.
Latency questions need a different aggregation. Averages hide the tail, so pull the 95th percentile instead:
requests | where timestamp > ago(15m) | where cloud_RoleName == "checkout-api" | summarize P95Duration = percentile(duration, 95) by name | where P95Duration > 2000
The P95Duration filter surfaces operations where 19 out of 20 requests are faster than the threshold and one in 20 is not, which is exactly the pattern a customer complaint describes and an average response time conveniently hides.
Turning Telemetry Into Alerts Before Users Notice
Collecting telemetry is only useful if something acts on it before a human notices the problem manually. Application Insights gives you two layers for that: Smart Detection, which needs no configuration, and alert rules, which you configure yourself.
Smart Detection Needs No Configuration
Smart Detection is a machine-learning feature that analyzes your baseline telemetry in the background and warns you when something deviates from it, without you setting a single threshold. It watches for three distinct patterns: your app answering slower than its own recent history (a bad deployment or a memory leak), a downstream dependency slowing down even though your code hasn’t changed, and slow performance that only affects some requests, such as one server in a pool lagging its peers, a pattern an average would hide.
Performance anomaly detection specifically needs at least eight days of consistent telemetry to establish that baseline before it will alert on anything.
Migrating Detections to Action Groups
Historically, Smart Detection’s notifications went out by email only, sent to Monitoring Reader and Monitoring Contributor roles. Microsoft has been migrating Smart Detection to standard Azure Monitor alert rules, which lets you attach action groups that page through SMS, voice calls, or a webhook into PagerDuty, ServiceNow, or a Logic App instead of just email. The detection algorithm doesn’t change during migration, only where the notification goes.
The migration isn’t a straight lift, though, and it runs one-way. Five of the older Smart Detection capabilities don’t survive the move: “slow page load time,” “slow server response time,” “long dependency duration,” “potential security issue detected,” and “abnormal rise in daily data volume” are retired during migration, not converted. Only failure anomalies, response and dependency latency degradation, trace severity degradation, exception volume anomalies, and potential memory leak detection survive as alert rules. Confirm you don’t depend on a retired detector before you migrate, because there’s no rollback once it’s gone.
Custom Alerts for Your Own Service-Level Target
Smart Detection is a safety net, not a replacement for alerts tied to your actual service-level target. For that, you write a custom log search alert against a KQL query, evaluated on a schedule you choose, such as every five minutes. If average response time crosses two seconds or availability drops below 99%, the rule fires and triggers the same action group your migrated Smart Detection alerts use.

Keeping the Telemetry Bill Honest
Application Insights observability is not free, and high-traffic applications generate far more telemetry than anyone actually queries. Sampling is how Application Insights keeps costs and ingestion volume under control without throwing away the signal you’d actually use during an incident.
Trace-Based Sampling vs. Ingestion Sampling
The Azure Monitor Distro client library readme states it plainly: “The Azure Monitor Distro uses rate-limited sampling by default, collecting up to 5.0 traces per second. This provides cost-effective telemetry collection for most applications while maintaining observability.” You can switch to fixed-percentage sampling instead, and because the distro decides per trace rather than per telemetry item, a sampled-in operation keeps its full request, dependency, and exception chain intact. Ingestion sampling, which drops data after it’s already reached the Azure Monitor endpoint, has no such coordination and is more likely to leave you with a broken trace missing half its spans. Treat it as a last resort for when you can’t touch the application at all, not a default setting.
Cost Controls Worth Setting Today
-
Prefer trace-based sampling over ingestion sampling. Ingestion sampling drops telemetry with no awareness of which spans belong together, so you can end up with a request span but none of the dependency calls that explain why it was slow.
-
Set daily caps on both the Application Insights resource and its Log Analytics workspace. The effective cap is whichever is lower, so an aggressive workspace cap silently truncates every resource sharing that workspace, not just the one you meant to limit.
-
Deploy workspace-based resources, not the retired classic model. Workspace-based resources consolidate role-based access control and let you correlate application telemetry with infrastructure logs in the same query, without cross-resource joins.
-
Track business milestones with one custom event instead of logging every request. Aggregating per-request events into a single
trackEventcall per transaction preserves the behavioral data you need while cutting ingestion volume.
Pro Tip: Confirm sampling is actually doing what you configured instead of assuming it. Run this against your workspace and look for any RetainedPercentage below 100, which tells you that telemetry type is being sampled.
union requests, dependencies, pageViews, browserTimings, exceptions, traces | where timestamp > ago(1d) | summarize RetainedPercentage = 100 / avg(itemCount) by bin(timestamp, 1h), itemType
Matching the Question to the Right Tool
Every tool in this guide answers a different question, and reaching for the wrong one during an incident wastes the minutes you don’t have. The Performance and Failures views sit underneath most of the rows below, so start there when a row’s answer isn’t specific enough.
| You’re asking | Reach for | What you get |
|---|---|---|
| Is the service degraded right now, mid-deployment? | Live Metrics | About one second of latency, no storage cost |
| Which downstream service is dragging this request down? | Application Map | A visual topology built from dependency calls |
| Exactly how many requests failed, and which operation? | A KQL query against requests or exceptions |
An exact count you can threshold and alert on |
| Has performance quietly degraded over the past week? | Smart Detection performance anomalies | A baseline comparison after eight days of telemetry |
| Should someone get paged? | An alert rule tied to an action group | Routed notification through SMS, webhook, or your ITSM tool |
Quick Win: The next time you ship a change to a service instrumented with the OpenTelemetry Distro, open Live Metrics before you deploy and leave it open through the rollout. A spike in exception rate shows up in about a second, long before a support ticket does.
Common Questions About Instrumenting With Application Insights
-
Do I have to rewrite my application to adopt OpenTelemetry? No. The Distro installs alongside your existing code. The classic SDK still works, it’s simply not where new capability is being invested.
-
Does sampling break distributed traces across services? Only ingestion sampling does. Sampling built into the OpenTelemetry Distro decides per trace, so a sampled-in operation keeps its dependency and exception chain together.
-
Will migrating Smart Detection change how detection behaves? No. Migration changes where the notification routes, through action groups instead of a fixed email list. Detection accuracy stays the same, though five legacy detectors are retired rather than converted.
Building the Habit of Watching Instead of Reacting
An uptime check will keep telling you the process is alive long after it stops doing anything useful for the people depending on it. Application Insights closes that gap by turning requests, dependencies, exceptions, and custom events into something you can query, chart, and alert on, instead of a single green checkmark.
Start narrow. Get the OpenTelemetry Distro instrumenting one service with a workspace-based resource behind it, set the connection string through an environment variable, and run the RetainedPercentage query so you know how much of your telemetry is being discarded. Migrate whatever Smart Detection rules you’re still running to action groups, then write one custom log alert tied to the response time or availability number your team actually cares about. That’s enough visibility for the next slow checkout page to show up in a query before it shows up in a support ticket.