Distributed Tracing in Azure

When a single user request touches ten microservices before returning a response, a traditional log file tells you almost nothing useful. Distributed tracing changes this by attaching a unique trace ID to every request and propagating it through every service, every queue message, and every downstream call — giving you a complete, end-to-end picture of exactly what happened, in order, with timing at every step.

How distributed tracing works

Distributed tracing builds on a simple idea: every request gets a globally unique ID (the trace ID), and every unit of work within that request gets a locally unique ID (the span ID). When service A calls service B, it passes both IDs in the request headers. Service B creates a new span with its own span ID, but records service A’s span ID as its parent. The result is a tree structure — the trace — that shows exactly how the request flowed through the system.

The standard headers used for this propagation are defined in the W3C Trace Context specification:

  • traceparent — Contains the version, trace ID, parent span ID, and trace flags. Example: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
  • tracestate — Vendor-specific data that travels alongside the trace. Application Insights uses this to pass the operation name.

Application Insights reads these headers on inbound requests and injects them on outbound calls automatically when you use the SDK, so in many cases trace propagation works without any manual code.

Spans, requests, and dependencies

In Application Insights terminology:

  • Request — An inbound operation received by a service (HTTP, gRPC, queue message consumption). This becomes the root span for that service’s contribution to the trace.
  • Dependency — An outbound call made by a service (HTTP call, SQL query, Redis command, Service Bus message send). Each dependency call becomes a child span.
  • Exception — An error recorded within a span.
  • Trace / Log — Custom messages recorded within the context of a span.

All four types are linked by the operation ID (the trace ID). This means you can query all of them together using the operation ID as a join key.

End-to-end transaction diagnostics: a real example

Scenario: A user reports that checkout is slow. The request ID from the browser’s network tab is 4bf92f3577b34da6a3ce929d0e0e4736. Here is how you investigate using KQL.

// Step 1: Find the root request and its duration
AppRequests
| where OperationId == "4bf92f3577b34da6a3ce929d0e0e4736"
| project TimeGenerated, Name, DurationMs, ResultCode, Success,
    RoleName = cloud_RoleName

// Step 2: See all spans (requests + dependencies) in the trace, ordered by time
union AppRequests, AppDependencies
| where OperationId == "4bf92f3577b34da6a3ce929d0e0e4736"
| project TimeGenerated,
    SpanType = itemType,
    Name,
    Target,
    DurationMs,
    Success,
    ResultCode,
    RoleName = cloud_RoleName,
    ParentId = operation_ParentId
| order by TimeGenerated asc

// Step 3: Find the slowest spans in this trace
union AppRequests, AppDependencies
| where OperationId == "4bf92f3577b34da6a3ce929d0e0e4736"
| project Name, DurationMs, SpanType = itemType, RoleName = cloud_RoleName
| order by DurationMs desc
| limit 10

// Step 4: Check for exceptions in this trace
AppExceptions
| where OperationId == "4bf92f3577b34da6a3ce929d0e0e4736"
| project TimeGenerated, ExceptionType, OuterMessage, InnermostMessage, RoleName = cloud_RoleName

Sampling: keeping costs under control

On a high-traffic application, storing every trace for every request is prohibitively expensive. Application Insights uses sampling to retain a statistically representative subset of traces. There are three sampling modes:

Sampling TypeHow it WorksBest For
Adaptive samplingAutomatically adjusts the sampling rate to target 5 telemetry items/second per SDK instanceMost applications; default behavior
Fixed-rate samplingRetains a configured percentage (e.g., 10%) of requests consistentlyWhen you need predictable volume; coordinated with backend services
Ingestion samplingAzure drops data at ingestion before it enters the workspaceLast resort; data is discarded, not just unsampled

Importantly, sampling is trace-aware: all spans belonging to the same trace are either kept or dropped together. You will never see half a trace in your data — if any span from a trace is sampled in, all related spans are sampled in.

Note

Errors are never sampled out by adaptive sampling. Application Insights always retains all telemetry for requests that result in exceptions or error status codes, even when sampling is active. This ensures you can always investigate failures.

Enabling cross-service tracing

For tracing to work across service boundaries, each service must use a compatible SDK that reads and propagates the W3C traceparent header. Application Insights SDKs do this automatically for HTTP calls. For messaging systems like Azure Service Bus and Event Hubs, the SDK auto-instruments message send and receive operations.

# Verify that an Application Insights connection string is configured
az webapp config appsettings list \
  --resource-group myRG \
  --name myapp \
  --query "[?name=='APPLICATIONINSIGHTS_CONNECTION_STRING']" \
  --output table

# Set the connection string if missing
az webapp config appsettings set \
  --resource-group myRG \
  --name myapp \
  --settings APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=<key>;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/"

# For .NET apps, also set the role name so traces show which service they came from
az webapp config appsettings set \
  --resource-group myRG \
  --name myapp \
  --settings APPLICATIONINSIGHTS_ROLE_NAME="OrderService"

OpenTelemetry integration

For new applications, Microsoft recommends using the Azure Monitor OpenTelemetry distro instead of the legacy Application Insights SDK. OpenTelemetry is a vendor-neutral standard, which means your instrumentation code is not tied to Azure. If you ever need to send traces to a different backend, you change the exporter configuration — not the instrumentation code.

# Install the Azure Monitor OpenTelemetry distro for .NET
dotnet add package Azure.Monitor.OpenTelemetry.AspNetCore

# For Node.js
npm install @azure/monitor-opentelemetry

# For Python
pip install azure-monitor-opentelemetry

The Azure Monitor distro automatically instruments HTTP clients, SQL, Redis, Azure SDK calls, and more — the same auto-instrumentation as the legacy SDK, but through the standardized OpenTelemetry pipeline.

Common mistakes

  1. Not setting the cloud role name. By default, Application Insights uses the host name as the role name. In containerized or serverless environments, host names are ephemeral and meaningless. Always set APPLICATIONINSIGHTS_ROLE_NAME to a human-readable service name so traces clearly show which service each span came from.
  2. Using the legacy AI-Request-Id header instead of W3C traceparent. Older Application Insights SDK versions used a proprietary header format. If any service in your chain uses the old format and others use W3C, trace correlation breaks at that boundary. Upgrade all services to the same SDK version family.
  3. Not propagating context through async message queues. HTTP calls propagate context automatically. But if your service reads from a queue and does not extract the trace context from the message properties, the trace chain is broken. Use the Application Insights SDK’s queue-aware instrumentation or manually extract and set the parent context.
  4. Sampling rate mismatch between services. If service A samples at 10% and service B (which it calls) samples at 50%, you will see orphaned spans in service B with no parent in service A. Coordinate sampling rates across all services in a trace or use head-based sampling where the first service makes the sampling decision for the entire trace.

Frequently asked questions

What is a trace in distributed tracing?

A trace represents the complete journey of a single request through your system. It is made up of spans — individual units of work at each service boundary. A trace for an e-commerce checkout might include spans for the API gateway, order service, inventory service, payment service, and database calls.

Does Application Insights support OpenTelemetry?

Yes. Application Insights supports the OpenTelemetry SDK for .NET, Java, Node.js, and Python. Microsoft recommends using the OpenTelemetry-based Azure Monitor distro for new applications, as it is the strategic direction going forward.

What is W3C Trace Context?

W3C Trace Context is the HTTP header standard (traceparent and tracestate) for propagating trace correlation across service boundaries. Application Insights uses this standard by default in modern SDK versions.

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