Azure Application Insights Profiler
You know your application is slow. Response times are high, users are complaining, but your logs and metrics tell you that resources look fine — CPU is at 30%, memory is healthy, database calls are fast. The problem is somewhere in your own code, and no amount of infrastructure monitoring will find it. Application Insights Profiler captures CPU flame graphs from your production application, showing you exactly which methods are consuming time — without needing a debugger or a dev environment.
What the Profiler captures
Application Insights Profiler runs a CPU sampling profiler on your production application for short 2-minute sessions. During these sessions, it records a stack trace sample every few milliseconds — capturing which methods are on the call stack and how often. From thousands of these samples, it builds a flame graph: a hierarchical visualization showing which code paths are responsible for CPU time.
The Profiler also captures traces for specific slow requests. When a request takes longer than a configurable threshold (default: 5 seconds), Application Insights automatically links a profiling trace to that specific request. You can open a slow request in Transaction Diagnostics and click “Profiler traces” to see exactly which call stack was running during the slow response time.
This is the difference between knowing “my API is slow” and knowing “the slowness is caused by the SerializeOrderHistory method calling JsonConvert.SerializeObject on a 50,000-item list”.
Enabling Profiler on Azure App Service
For .NET applications on App Service, enabling the Profiler requires the Application Insights extension and the right application settings.
# Verify Application Insights is configured
az webapp config appsettings list \
--resource-group myRG \
--name myapp \
--query "[?name=='APPLICATIONINSIGHTS_CONNECTION_STRING']" \
--output table
# Enable the Application Insights site extension (required for Profiler)
az webapp config appsettings set \
--resource-group myRG \
--name myapp \
--settings \
ApplicationInsightsAgent_EXTENSION_VERSION="~3" \
XDT_MicrosoftApplicationInsights_Mode="Recommended" \
APPINSIGHTS_PROFILERFEATURE_VERSION="1.0.0" \
DiagnosticServices_EXTENSION_VERSION="~3"
# Restart the app to pick up the new settings
az webapp restart \
--resource-group myRG \
--name myapp
# Verify the extension is loaded
az webapp config appsettings list \
--resource-group myRG \
--name myapp \
--query "[?name=='APPINSIGHTS_PROFILERFEATURE_VERSION']" \
--output tableThe Profiler requires your App Service plan to be at least Basic (B1) tier. The Free and Shared tiers do not support the necessary process monitoring APIs. For .NET 6+ applications, also ensure the ApplicationInsightsAgent_EXTENSION_VERSION is set to ~3 rather than the legacy ~2.
Reading a flame graph
A flame graph shows methods as horizontal bars. The width of a bar represents what percentage of total sampled time that method was on the stack. The vertical axis shows the call hierarchy — a bar at the top of the stack called the bar below it.
How to read it:
- Wide bars are slow code. A method that is 60% of the total width was consuming 60% of the time. Start your investigation at the widest bars.
- Look for wide bars without wide children. If a method is wide but its children are narrow, the time is being spent inside that method itself, not in something it calls. This is where the actual slowness lives.
- Your code vs. framework code. Look for your application’s namespace in the stack. Framework methods (like ASP.NET routing or Entity Framework) are often wide but expected. Your business logic methods should not be wide unless something is wrong.
A real example of what you might find: A method called GetProductCatalog is 45% of the flame graph. Its only child is JsonConvert.DeserializeObject at 42%. The fix: the method is deserializing the entire product catalog JSON on every request instead of caching the deserialized object.
On-demand profiling sessions
In addition to the automatic 2-minute-per-hour sessions, you can trigger on-demand profiling sessions. This is useful when you want to capture a profile during a known slow period — for example, during a load test, a specific batch job, or a scheduled high-traffic event.
Trigger on-demand profiling from the Application Insights portal under “Performance” → “Profiler” → “Profile Now”. You can select the duration (30 seconds to 5 minutes) and specific instances to profile.
# Check profiler status via the diagnostics API
# (Profiler management is portal/API-based, not CLI)
# Use the REST API to check current profiler configuration:
ACCESS_TOKEN=$(az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv)
curl -X GET \
"https://management.azure.com/subscriptions/<sub-id>/resourceGroups/myRG/providers/Microsoft.Web/sites/myapp/diagnostics/profiler/analyses?api-version=2021-01-01" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json"Finding profiler traces for slow requests
The most powerful workflow is to go from a slow request in Transaction Diagnostics to its associated profiler trace. Here is how to find slow requests first:
// Find the slowest requests in the last 4 hours with a profiler trace available
AppRequests
| where TimeGenerated > ago(4h)
| where DurationMs > 3000
| where Success == true // Focus on slow-but-successful requests, not errors
| project TimeGenerated, Name, DurationMs, OperationId, RoleName = cloud_RoleName
| order by DurationMs desc
| limit 20
// Check if profiler traces exist for a specific operation
// (Profiler traces are stored as AppDependencies with type "Profile")
AppDependencies
| where TimeGenerated > ago(4h)
| where DependencyType == "Profile"
| project TimeGenerated, OperationId, Name, DurationMs
| order by TimeGenerated descIn the Application Insights portal, go to Performance → select a slow operation → click “Profiler Traces” in the right panel. This links slow requests to their associated profiling sessions automatically, with no manual query needed.
Profiler vs. Snapshot Debugger
Application Insights offers two production debugging tools that are often confused:
| Feature | Profiler | Snapshot Debugger |
|---|---|---|
| Purpose | CPU performance — which code is slow? | Exception debugging — what were the variable values when it crashed? |
| Triggers on | Any request (by time sampling or on-demand) | Exceptions that exceed a threshold count |
| Output | Flame graph / call stack sample | Minidump with local variable values at the time of the exception |
| PDB files required | Yes (for symbol resolution) | Yes (for symbol resolution) |
| Use case | High latency, CPU-bound code, hot paths | NullReferenceException, unexpected state, missing data at crash site |
Common mistakes
- Not uploading PDB files. Profiler traces require debug symbols (PDB files) to resolve method names. Without them, the flame graph shows raw memory addresses instead of readable method names. Configure your CI/CD pipeline to upload PDB files to Application Insights as part of the build and deploy process.
- Only profiling under low load. On-demand profiling during normal traffic may not capture the code paths that are slow under high concurrency. Trigger profiling sessions during load tests or peak traffic periods to capture the hot paths that matter in production.
- Expecting Profiler to find I/O-bound slowness. CPU flame graphs show time spent in running code. If your application is slow because it is waiting for a database call (I/O-bound), the profiler will show a thin stack while waiting, not a wide hot path. For I/O-bound slowness, use dependency tracking in Transaction Diagnostics instead.
- Disabling the Profiler in production “for safety”. The Profiler is specifically designed for production use with minimal overhead. Profiling only in staging or dev misses the workload patterns, concurrency levels, and data sizes that cause production performance issues.
Summary
- Application Insights Profiler captures CPU flame graphs from production applications by running brief 2-minute sampling sessions hourly.
- Profiler automatically links traces to slow requests, letting you go from a specific slow operation directly to the method-level call stack.
- Flame graphs show method execution time as bar widths — wide bars without wide children are where the actual slowness lives.
- Profiler works on .NET and Java; PDB files must be uploaded for symbol resolution.
- Profiler finds CPU-bound slowness; use dependency tracking in Transaction Diagnostics for I/O-bound (database, HTTP) slowness.
Frequently asked questions
Does the Profiler slow down my application?
Application Insights Profiler is designed for production use. It adds approximately 5% CPU overhead during the brief profiling sessions (typically 2 minutes every hour). Outside of these sessions, there is no overhead.
What runtimes does the Profiler support?
Profiler supports .NET Framework (4.6+), .NET Core, .NET 5+, and Java on Azure App Service, Azure Functions, Azure Virtual Machines, and Azure Cloud Services. Node.js and Python are not currently supported.
How long does it take for profiling data to appear?
Profiler data typically appears in Application Insights within 5–10 minutes after a profiling session completes. If a slow request triggers an on-demand profile, results appear within a few minutes.