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 table
Note

The 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 desc
Tip

In 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:

FeatureProfilerSnapshot Debugger
PurposeCPU performance — which code is slow?Exception debugging — what were the variable values when it crashed?
Triggers onAny request (by time sampling or on-demand)Exceptions that exceed a threshold count
OutputFlame graph / call stack sampleMinidump with local variable values at the time of the exception
PDB files requiredYes (for symbol resolution)Yes (for symbol resolution)
Use caseHigh latency, CPU-bound code, hot pathsNullReferenceException, unexpected state, missing data at crash site

Common mistakes

  1. 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.
  2. 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.
  3. 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.
  4. 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.

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.

Last verified: 19 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.