Intermediate AZ-204

AZ-204: Monitoring and Troubleshooting Applications

Azure Monitor, Application Insights, distributed tracing, KQL, availability tests and the Profiler.

Course: Azure Developer Associate (AZ-204) – Monitoring and Troubleshooting Applications

Module 1 – Exploring Azure Monitor

Why Instrument Your Applications?

  • After a deployment, don’t wait for users to report issues.
  • Instrumentation means adding code that generates data about the application’s behavior.
  • Exam objective: Monitor and troubleshoot solutions by using Application Insights.

The 3 Pillars of Observability

PillarDescription
LogsText messages describing events. Stored in Log Analytics.
TracesA series of logs tied to a single user request. Lets you follow the complete journey of a request.
MetricsNumeric time-series data (e.g., CPU, memory, connected users). Also called performance counters.

Azure Monitor vs. Application Insights vs. Log Analytics

  • Log Analytics Workspace: Storage service for logs. All sources (activity log, resource logs, app) send their data here.
  • Azure Monitor: Generic central hub for storing metrics and logs.
  • Application Insights: Service optimized for instrumenting applications whose source code you control. Uses Azure Monitor as its backend.
    • Connection App Insights → Azure Monitor: configured when the Application Insights instance is created.
    • Connection Application → App Insights: via the connection string injected into the app configuration.

Diagnostic Settings

  • Available on all Azure resources.
  • Used to route logs to one or more Log Analytics Workspaces.
  • Configuration: select desired logs + destination.

Demo: Create a Log Analytics Workspace

  1. Create the workspace (only required parameter: name, e.g. all-demo-logs).
  2. Configure a Diagnostic Setting on the subscription to send the Activity Log there.
  3. Use KQL (Kusto Query Language) to query data in Log Analytics.

Azure Service Health

  • Monitors the health of the Azure platform itself.
  • Resource Health: health of a specific resource (includes historical view).
  • Service Health blade: overview of all active incidents for your resources.
  • Helps distinguish whether a problem comes from your code or the Azure platform.

Module 2 – Application Insights In Depth

Application Insights Architecture

Application → Application Insights → Azure Monitor (backend)
                                         ↓
                                  Engineer (query/troubleshoot)

3 Ways to Connect an App to Application Insights

MethodAdvantageDisadvantage
AutoinstrumentationFast, no code changesLess data
Application Insights SDKRich, customizable dataRequires a redeployment
OpenTelemetry DistroOpen standard, portableMore complex configuration

Autoinstrumentation (Demo)

  • Done without redeployment, directly from the Azure portal.
  • In the App Service → Application Insights tab → enable monitoring.
  • Azure automatically injects agents into the app.
  • Generates data such as HTTP requests, dependencies, and exceptions.

Installing the Application Insights SDK (Demo)

  1. In Visual Studio: right-click the project → Connected ServicesAddApplication Insights.
  2. The wizard installs NuGet packages and configures the code.
  3. The connection string is read from the application configuration.
  4. Use ILogger (dependency injection) to emit custom logs.
// Example usage of ILogger
_logger.LogInformation("Processing request {RequestId}", requestId);
_logger.LogWarning("Item not found for ID {ItemId}", itemId);
_logger.LogError(ex, "Error while creating the order");

Types of Data Collected by Application Insights

  • Requests: all HTTP requests received by the API.
  • Dependencies: calls to external services (DB, third-party APIs, etc.).
  • Exceptions: unhandled errors and traced exceptions.
  • Traces: logs emitted via ILogger or the telemetry APIs.
  • Custom Metrics: custom metrics defined in code.

Main Views in Application Insights

  • Live Metrics: real-time monitoring (live stream).
  • Transaction Search: search for individual requests.
  • Application Map: topological view of dependencies.
  • Failures: analysis of errors and exceptions.
  • Performance: response times by operation.

Web Tests and Availability Alerts

  • URL Ping Test: simple test that verifies a URL responds with HTTP 200.
  • Multi-step Web Test: sequence of HTTP requests simulating a user scenario.
  • Standard Test: tests SSL, domain name, HTTP response.
  • Tests run from multiple configurable geographic regions.
  • Configure alerts on tests to be notified of failures.

Sampling

  • For high-traffic applications, Application Insights can sample data.
  • Reduces costs while maintaining a statistically valid representation.
  • Types: Adaptive sampling (auto), Fixed-rate sampling, Ingestion sampling.

OpenTelemetry

  • Open standard for observability (logs, traces, metrics).
  • Microsoft is pushing OpenTelemetry as an alternative/complement to the proprietary SDK.
  • The Azure Monitor Distro for OpenTelemetry sends data to Application Insights.

Module 3 – Alerts and Dashboards

Azure Monitor Alerts

  • Triggered on metrics, logs, or activity signals.
  • Components:
    • Alert Rule: trigger condition.
    • Action Group: list of actions to execute (email, SMS, webhook, Azure Function, etc.).
    • Alert Instance: triggered occurrence.
  • Severity: 0 (critical) → 4 (verbose).

Alert Types

TypeBased on
Metric alertA metric value crossing a threshold
Log alertResult of a KQL query
Activity log alertEvents from the Azure activity log

Dynamic Thresholds

  • Machine learning that automatically calculates thresholds based on history.
  • Advantage: adapts to seasonal variations (e.g., more traffic on Friday nights).
  • Automatically configures thresholds instead of entering them manually.

Workbooks

  • Interactive reports combining text, queries, and visualizations.
  • Allow sharing analyses with stakeholders.

Key Points for the Exam

  • Application Insights sits between the application and Azure Monitor.
  • The 3 pillars: Logs, Traces, Metrics.
  • Autoinstrumentation does not require a redeployment.
  • Connection string (not Instrumentation Key) is the recommended method.
  • Web tests require an App Service plan (not the Free plan).
  • Azure Service Health for Azure platform health, Resource Health for a specific resource.
  • Log Analytics stores data; Application Insights provides specialized views for applications.

Module 4 – Application Insights SDK Integration

SDK Architecture

flowchart LR
    A[.NET Application] -->|TelemetryClient| B[TelemetryChannel]
    B -->|HTTPS| C[Application Insights Endpoint]
    C --> D["(Log Analytics Workspace)"]
    D --> E[Azure Monitor]

    subgraph SDK Concepts
        F[TelemetryClient] --> G[TelemetryContext]
        F --> H[TelemetryConfiguration]
        H --> I[ITelemetryInitializer]
        H --> J[ITelemetryProcessor]
    end

TelemetryClient – Overview

The TelemetryClient is the central class of the SDK. It exposes methods for each type of telemetry:

MethodData TypeTable in Log Analytics
TrackEvent()Custom eventcustomEvents
TrackMetric()Custom metriccustomMetrics
TrackException()Exceptionexceptions
TrackRequest()HTTP requestrequests
TrackDependency()Outbound calldependencies
TrackTrace()Trace logtraces
TrackAvailability()Availability testavailabilityResults
TrackPageView()Page view (JS)pageViews

Installation and Configuration

// Required NuGet package
// Microsoft.ApplicationInsights.AspNetCore

// Program.cs – service registration
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddApplicationInsightsTelemetry(options =>
{
    options.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"];
    options.EnableAdaptiveSampling = true;
    options.EnableQuickPulseMetricStream = true; // Live Metrics
});

TrackEvent – Custom Events

using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;

public class OrderController : ControllerBase
{
    private readonly TelemetryClient _telemetry;

    public OrderController(TelemetryClient telemetry)
    {
        _telemetry = telemetry;
    }

    [HttpPost]
    public async Task<IActionResult> CreateOrder(OrderDto dto)
    {
        // Event with custom properties
        _telemetry.TrackEvent("OrderPlaced", new Dictionary<string, string>
        {
            { "OrderId",    dto.Id.ToString() },
            { "CustomerId", dto.CustomerId.ToString() },
            { "Region",     dto.Region }
        },
        new Dictionary<string, double>
        {
            { "OrderTotal", (double)dto.TotalAmount },
            { "LineItems",  dto.Items.Count }
        });

        // ...
        return Ok();
    }
}

TrackMetric – Custom Metrics

// Direct method
_telemetry.TrackMetric("QueueDepth", queue.Count);

// Via GetMetric (recommended – aggregates before sending, reduces costs)
private readonly Metric _processingDuration;

public ProcessingService(TelemetryClient telemetry)
{
    // Creates a metric with a "Priority" dimension
    _processingDuration = telemetry.GetMetric(
        new MetricIdentifier("ProcessingDuration", "Priority"));
}

public void ProcessItem(WorkItem item)
{
    var sw = Stopwatch.StartNew();
    // ... processing ...
    sw.Stop();

    _processingDuration.TrackValue(sw.ElapsedMilliseconds, item.Priority);
}

Exam tip: GetMetric() is preferred over TrackMetric() because it pre-aggregates values on the client side and reduces the number of network calls.

TrackException – Exceptions

try
{
    await _repository.SaveAsync(entity);
}
catch (SqlException ex)
{
    _telemetry.TrackException(ex, new Dictionary<string, string>
    {
        { "Operation",  "SaveEntity" },
        { "EntityType", entity.GetType().Name },
        { "RecordId",   entity.Id.ToString() }
    });
    throw; // re-throw to preserve the stack trace
}

TrackRequest – Manual Requests

// Useful for workers, background services, or queue messages
var startTime = DateTimeOffset.UtcNow;
var timer = Stopwatch.StartNew();
bool success = false;

try
{
    await ProcessMessageAsync(message);
    success = true;
}
finally
{
    timer.Stop();
    _telemetry.TrackRequest(
        name:       "ProcessQueueMessage",
        startTime:  startTime,
        duration:   timer.Elapsed,
        responseCode: success ? "200" : "500",
        success:    success);
}

TrackDependency – Dependencies

var startTime = DateTimeOffset.UtcNow;
var timer = Stopwatch.StartNew();
bool success = false;

try
{
    result = await _httpClient.GetAsync(externalApiUrl);
    success = result.IsSuccessStatusCode;
}
finally
{
    timer.Stop();
    _telemetry.TrackDependency(
        dependencyTypeName: "HTTP",
        target:             "api.external.com",
        dependencyName:     "GET /v1/data",
        data:               externalApiUrl.ToString(),
        startTime:          startTime,
        duration:           timer.Elapsed,
        resultCode:         result?.StatusCode.ToString() ?? "Timeout",
        success:            success);
}

TelemetryContext – Global Properties

// Add properties to ALL telemetry items via an Initializer
public class AppTelemetryInitializer : ITelemetryInitializer
{
    private readonly IHttpContextAccessor _httpContext;

    public AppTelemetryInitializer(IHttpContextAccessor httpContext)
    {
        _httpContext = httpContext;
    }

    public void Initialize(ITelemetry telemetry)
    {
        // Enrich each item with the tenant ID extracted from the JWT
        var userId = _httpContext.HttpContext?.User?.FindFirst("sub")?.Value;
        if (!string.IsNullOrEmpty(userId))
        {
            telemetry.Context.User.AuthenticatedUserId = userId;
        }

        // Global application properties
        telemetry.Context.GlobalProperties["AppVersion"] = "2.4.1";
        telemetry.Context.GlobalProperties["Environment"] = "Production";
    }
}

// Registration in Program.cs
builder.Services.AddSingleton<ITelemetryInitializer, AppTelemetryInitializer>();

Sampling Configuration

builder.Services.AddApplicationInsightsTelemetry();
builder.Services.Configure<TelemetryConfiguration>(config =>
{
    // Adaptive sampling (default) – adjusts rate automatically
    var adaptiveSampling = new AdaptiveSamplingTelemetryProcessor(config)
    {
        MaxTelemetryItemsPerSecond = 5,   // ceiling of items/s
        MinSamplingPercentage     = 5,    // never below 5%
        MaxSamplingPercentage     = 100   // never above 100%
    };

    // Fixed-rate sampling – fixed rate
    // var fixedSampling = new SamplingTelemetryProcessor(config)
    // {
    //     SamplingPercentage = 25 // keeps 25% of items
    // };
});
Sampling TypeAdvantageUse Case
AdaptiveAdjusts automaticallyVariable traffic
Fixed-ratePredictable and consistent ratePrecise statistical analysis
IngestionConfigured on the Application Insights side, no redeploymentCost reduction without code changes

Module 5 – Distributed Tracing and Correlation

Fundamental Concepts

flowchart TD
    Browser -->|HTTP GET /order/42| API[API Gateway]
    API -->|HTTP POST /validate| VS[Validation Service]
    API -->|HTTP POST /payment| PS[Payment Service]
    PS -->|SQL Query| DB["(SQL Database)"]

    subgraph W3C TraceContext Headers
        H1["traceparent: 00-{traceId}-{spanId}-01"]
        H2["tracestate: vendor-specific data"]
    end
ConceptDescription
TraceIdUnique GUID identifying the entire end-to-end transaction
SpanIdGUID identifying a single operation within the trace
Activity.NET representation of a span (System.Diagnostics.Activity)
ActivitySourceFactory for creating named Activities
W3C TraceContextHTTP standard for propagating correlation IDs

ActivitySource (.NET) – Manual Instrumentation

using System.Diagnostics;

// Define the source at the class / assembly level
public static class Telemetry
{
    public static readonly ActivitySource Source =
        new ActivitySource("MyApp.OrderProcessing", "1.0.0");
}

public class OrderProcessor
{
    public async Task ProcessAsync(Order order)
    {
        // Creates a span (Activity) – automatic parent context propagation
        using var activity = Telemetry.Source.StartActivity("ProcessOrder");
        activity?.SetTag("order.id",     order.Id);
        activity?.SetTag("order.amount", order.TotalAmount);
        activity?.SetTag("customer.id",  order.CustomerId);

        try
        {
            await ValidateAsync(order);
            await ChargeAsync(order);

            activity?.SetStatus(ActivityStatusCode.Ok);
        }
        catch (Exception ex)
        {
            activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
            activity?.RecordException(ex);
            throw;
        }
    }
}

Registering the ActivitySource with OpenTelemetry

// Program.cs
builder.Services.AddOpenTelemetry()
    .UseAzureMonitor()
    .WithTracing(tracing =>
    {
        tracing
            .AddSource("MyApp.OrderProcessing")   // custom source
            .AddAspNetCoreInstrumentation()        // inbound HTTP
            .AddHttpClientInstrumentation()        // outbound HTTP
            .AddSqlClientInstrumentation();        // SQL
    })
    .WithMetrics(metrics =>
    {
        metrics
            .AddAspNetCoreInstrumentation()
            .AddRuntimeInstrumentation();
    });

W3C TraceContext – Cross-Service Propagation

// The SDK automatically injects headers for HttpClient calls
// Header: traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
//          ^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^  ^^
//        version      traceId (128-bit hex)     parentId (64-bit) flags

// To read the IDs in the current context:
var traceId  = Activity.Current?.TraceId.ToString();
var spanId   = Activity.Current?.SpanId.ToString();
var parentId = Activity.Current?.ParentId;

_logger.LogInformation(
    "Processing order {OrderId} | trace={TraceId} span={SpanId}",
    orderId, traceId, spanId);

KQL Query – End-to-End Correlation

// Find all operations related to a TraceId
let targetTrace = "4bf92f3577b34da6a3ce929d0e0e4736";
union requests, dependencies, exceptions, traces
| where operation_Id == targetTrace
| project timestamp, itemType, name, duration, success, message
| order by timestamp asc

Transaction Map (End-to-End Transaction)

In Application Insights → Transaction Search → select a request → View all telemetry:

  • Chronological view of all spans in the transaction.
  • See nested dependencies, their duration, and status.
  • Identify which service added latency.

Module 6 – Live Metrics Stream

Live Metrics Architecture

flowchart LR
    App[Production Application] -->|QuickPulse SDK\nHTTPS polling| QP[QuickPulse Endpoint]
    QP -->|WebSocket| Portal[Azure Portal\nLive Metrics]
    Portal --> Engineer[Engineer]
  • Display latency: ~1 second (near real-time).
  • Data is not persisted in Log Analytics (display only).
  • Enabled via EnableQuickPulseMetricStream = true in configuration.

Metrics Available in Real Time

CategoryMetrics
RequestsRate (req/s), average duration, failure rate
DependenciesCall rate, duration, failure rate
ExceptionsExceptions rate/s
PerformanceProcess CPU (%), memory (MB)
Event streamRequests, traces, dependencies, exceptions live

Real-Time Filtering (Custom Dimensions)

The right panel in Live Metrics shows the raw telemetry stream. You can filter by:

  • Operation name (e.g., GET /api/orders)
  • Context properties (cloud_RoleName, cloud_RoleInstance)
  • Result (success / failure)
// Properties added via TelemetryContext appear in Live Metrics
_telemetry.Context.Cloud.RoleName     = "order-api";
_telemetry.Context.Cloud.RoleInstance = Environment.MachineName;

Typical Use Cases

  • Validate a deployment: immediately observe whether the error rate rises after a deployment.
  • Production debugging: track specific requests in real time without impacting stored logs.
  • Demonstrations: show live behavior during a presentation.

Exam tip: Live Metrics stores no data in Log Analytics. Data is ephemeral.


Module 7 – Log Analytics Workspace and KQL

Workspace Architecture

flowchart TD
    subgraph Sources
        AI[Application Insights]
        AS[App Service Logs]
        AKS[AKS Logs]
        AL[Activity Log]
        VM[VM Diagnostics]
    end

    subgraph Log Analytics Workspace
        T1[requests]
        T2[exceptions]
        T3[dependencies]
        T4[traces]
        T5[customEvents]
        T6[customMetrics]
        T7[AzureActivity]
        T8[ContainerLog]
    end

    AI --> T1 & T2 & T3 & T4 & T5 & T6
    AL --> T7
    AKS --> T8
    AS --> T4
    VM --> T4

KQL – Essential Operators for AZ-204

where – Filter rows

requests
| where timestamp > ago(1h)
| where success == false
| where resultCode == "500"

project – Select columns

exceptions
| where timestamp > ago(24h)
| project timestamp, type, outerMessage, operation_Id, cloud_RoleName

extend – Add calculated columns

requests
| where timestamp > ago(1h)
| extend DurationSec = duration / 1000.0
| extend IsSlow = DurationSec > 2.0
| project timestamp, name, DurationSec, IsSlow

summarize – Aggregate data

// Error rates by operation over the last 24h
requests
| where timestamp > ago(24h)
| summarize
    TotalRequests = count(),
    FailedRequests = countif(success == false),
    AvgDuration = avg(duration),
    P95Duration = percentile(duration, 95)
  by name
| extend ErrorRate = round(100.0 * FailedRequests / TotalRequests, 2)
| order by ErrorRate desc

join – Join tables

// Join requests with their exceptions for analysis
requests
| where timestamp > ago(1h) and success == false
| join kind=leftouter (
    exceptions
    | where timestamp > ago(1h)
    | project exceptionType = type, exceptionMsg = outerMessage, operation_Id
) on operation_Id
| project timestamp, name, resultCode, exceptionType, exceptionMsg

bin + summarize – Time series

// Error rates per 5-minute interval
requests
| where timestamp > ago(6h)
| summarize
    Total   = count(),
    Errors  = countif(success == false)
  by bin(timestamp, 5m)
| extend ErrorRate = 100.0 * Errors / Total
| render timechart

Useful KQL Queries for the Exam

// 1 – Top 10 slowest operations
requests
| where timestamp > ago(24h)
| summarize P99 = percentile(duration, 99) by name
| top 10 by P99 desc

// 2 – Exceptions by type over the last 7 days
exceptions
| where timestamp > ago(7d)
| summarize Count = count() by type
| order by Count desc

// 3 – Failed dependencies
dependencies
| where timestamp > ago(1h) and success == false
| project timestamp, name, target, resultCode, duration
| order by timestamp desc

// 4 – Traces by severity level
traces
| where timestamp > ago(1h)
| summarize Count = count() by severityLevel
// 0=Verbose, 1=Information, 2=Warning, 3=Error, 4=Critical

// 5 – Request → exception correlation (by operation_Id)
let failedOps = requests
    | where success == false and timestamp > ago(1h)
    | project operation_Id, reqName = name;
exceptions
| where timestamp > ago(1h)
| join kind=inner failedOps on operation_Id
| project timestamp, reqName, type, outerMessage

Cross-workspace Queries

// Query multiple workspaces in a single query
union
    workspace("workspace-prod").requests,
    workspace("workspace-staging").requests
| where timestamp > ago(1h)
| summarize Count = count() by bin(timestamp, 5m), $__table
| render timechart

Log-Based Alerts (Log Alerts)

Key parameters when creating a Log alert:

  • Evaluation frequency: every 5 min / 15 min / 1h.
  • Time window: period over which the query is run.
  • Threshold: e.g., > 0 critical exceptions → triggers the alert.
  • Aggregation: count, average, min, max.

Module 8 – Azure Monitor Alerts

Alert Architecture

flowchart TD
    subgraph Signal Sources
        M[Azure Monitor Metrics]
        L[Log Analytics KQL]
        A[Activity Log]
        H[Azure Service Health]
    end

    subgraph Alert Rule
        C[Condition\nStatic or dynamic threshold]
        AG[Action Group]
    end

    subgraph Action Group Actions
        E[Email / SMS]
        W[Webhook]
        AF[Azure Function]
        LA[Logic App]
        IT[ITSM Connector]
        AR[Azure Runbook]
    end

    M & L & A & H --> C
    C -->|Triggered| AG
    AG --> E & W & AF & LA & IT & AR

Metric Alerts – Static vs. Dynamic Threshold

CriterionStatic ThresholdDynamic Threshold (ML)
ConfigurationFixed value defined manuallyCalculated automatically by Machine Learning
AdaptabilityNo – same threshold 24/7Yes – adapts to seasonal patterns
Historical dataNot requiredRequires at least 3 days
Use caseCPU > 90%, Errors > 100/minNormal traffic on weekends vs. weekdays
SensitivityN/AHigh / Medium / Low
// Conceptual example of a static metric alert condition:
// Metric: requests/failed
// Operator: GreaterThan
// Threshold: 10
// Aggregation: Count
// Window: 5 minutes
// Frequency: 1 minute

Log Alerts (KQL-based)

// Rule: trigger an alert if more than 5 critical exceptions in 5 minutes
exceptions
| where timestamp > ago(5m)
| where severityLevel >= 3
| summarize CriticalErrors = count()
| where CriticalErrors > 5

Log alert rule parameters:

  • Frequency: every 5 minutes.
  • Lookback window: 5 minutes.
  • Threshold: Number of results > 0.

Activity Log Alerts

Triggered on events such as:

  • Microsoft.Web/sites/restart/action – App Service restart
  • Microsoft.KeyVault/vaults/delete – Key Vault deletion
  • Microsoft.Authorization/roleAssignments/write – RBAC modification
// Example of an Activity Log alert condition
{
  "field": "operationName",
  "equals": "Microsoft.Web/sites/restart/action"
}

Action Groups – Configuration

flowchart LR
    AG["Action Group\n'ops-team-alerts'"] --> Email["📧 Email\nops@company.com"]
    AG --> SMS["📱 SMS\n+1-555-000-0000"]
    AG --> WH["🔗 Webhook\nhttps://teams.webhook.url"]
    AG --> AF["⚡ Azure Function\nAlertProcessor"]
    AG --> ITSM["🎫 ServiceNow\nITSM Connector"]
    AG --> LA["🔄 Logic App\nAutoRemediation"]

Notification Throttling

  • Alert suppression: avoids spam in case of repetitive alerts.
  • Alert processing rules: suppress or modify notifications based on criteria (time window, resource tag).
  • Maintenance window: scheduled notification suppression during a maintenance window.
# Create an Action Group via Azure CLI
az monitor action-group create \
  --name "ops-team-alerts" \
  --resource-group "rg-monitoring" \
  --short-name "ops-alerts" \
  --email-receiver name="ops-email" \
                   email-address="ops@company.com" \
                   use-common-alert-schema true

# Create a static metric alert
az monitor metrics alert create \
  --name "high-error-rate" \
  --resource-group "rg-monitoring" \
  --scopes "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/{app}" \
  --condition "count requests/failed > 10" \
  --window-size 5m \
  --evaluation-frequency 1m \
  --action "ops-team-alerts"

Module 9 – Application Map

Overview

The Application Map provides a topological view of all application components and their dependencies. It is built automatically from collected telemetry data.

graph LR
    FE[Blazor Frontend\ncloud_RoleName=blazor-app] -->|HTTP| API[REST API\ncloud_RoleName=order-api]
    API -->|SQL| DB["(Azure SQL)"]
    API -->|HTTP| PAY[Payment Service\ncloud_RoleName=payment-svc]
    API -->|AMQP| SB[Service Bus]
    PAY -->|HTTP| EXT[Stripe API\nexternal]

    style DB fill:#f0f0f0
    style EXT fill:#ffe0e0

Information Displayed on Each Node

InformationDescription
Request countTraffic volume to this component
Failure rate% of failed requests (red circle if > threshold)
Average durationAverage latency of calls
Active alertsAzure Monitor alerts linked to the component
InsightsAnomalies detected by Smart Detection

Configuring Component Name (cloud_RoleName)

// Method 1: via TelemetryInitializer
public class RoleNameInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        telemetry.Context.Cloud.RoleName     = "order-api";
        telemetry.Context.Cloud.RoleInstance = Environment.MachineName;
    }
}

// Method 2: via appsettings.json (OpenTelemetry)
// {
//   "AzureMonitor": {
//     "ServiceName": "order-api"
//   }
// }

// Method 3: environment variable
// OTEL_SERVICE_NAME=order-api

Important: Without cloud_RoleName configured, all components appear under the same name on the map. This is the most common mistake when configuring a multi-service architecture.

Identifying Performance Hotspots

  1. Click on a node to see inbound/outbound operations.
  2. Click on an edge (arrow) to see dependency details.
  3. Red nodes indicate a high failure rate.
  4. Use the Time Range filter to compare before/after a deployment.

Module 10 – Availability Tests

Test Types

TypeStatusDescription
URL Ping Test⚠️ Deprecated (Sept. 2026)Simple GET test on a URL
Multi-step Web Test⚠️ DeprecatedHTTP request sequence (.webtest)
Standard Test✅ RecommendedConfigurable HTTP test with SSL validation
Custom TrackAvailability✅ RecommendedRun from your own code

Standard Availability Test – Configuration

flowchart LR
    subgraph Azure Availability Infrastructure
        LOC1[East US]
        LOC2[West Europe]
        LOC3[Southeast Asia]
        LOC4[Brazil South]
        LOC5[Australia East]
    end

    LOC1 & LOC2 & LOC3 & LOC4 & LOC5 -->|HTTP GET/POST| App[https://myapp.azurewebsites.net/health]
    App -->|200 OK + body match| Result[✅ Pass]
    App -->|Timeout / 5xx / SSL invalid| Alert[🚨 Alert]

Standard Test configuration options:

  • URL: endpoint to test.
  • HTTP Verb: GET, POST, HEAD.
  • Request headers: e.g., Authorization, Content-Type.
  • Request body: payload for POST tests.
  • Parse dependent requests: load CSS, JS, images.
  • SSL certificate check: validate the certificate is valid + X days.
  • Frequency: every 5, 10, or 15 minutes.
  • Locations: minimum 5 recommended to avoid false positives.

Response Validation

{
  "successCriteria": {
    "httpStatusCode": 200,
    "contentMatch": "healthy",
    "sslCertificateRemainingLifetimeDays": 7
  }
}

Custom TrackAvailability – Custom Tests

// Azure Function or Worker Service running a custom test
public class CustomAvailabilityTest
{
    private readonly TelemetryClient _telemetry;
    private readonly HttpClient _httpClient;

    public async Task RunTestAsync()
    {
        var testName  = "Login Flow E2E Test";
        var startTime = DateTimeOffset.UtcNow;
        var timer     = Stopwatch.StartNew();
        bool passed   = false;
        string message = string.Empty;

        try
        {
            // Simulate a complete login scenario
            var loginResponse = await _httpClient.PostAsJsonAsync("/api/auth/login",
                new { username = "testuser", password = "testpass" });

            loginResponse.EnsureSuccessStatusCode();

            var token = await loginResponse.Content.ReadFromJsonAsync<TokenResponse>();

            // Verify the token is valid
            _httpClient.DefaultRequestHeaders.Authorization =
                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token!.AccessToken);

            var profileResponse = await _httpClient.GetAsync("/api/users/me");
            profileResponse.EnsureSuccessStatusCode();

            passed  = true;
            message = "Login flow completed successfully";
        }
        catch (Exception ex)
        {
            message = ex.Message;
        }
        finally
        {
            timer.Stop();

            _telemetry.TrackAvailability(
                name:       testName,
                timeStamp:  startTime,
                duration:   timer.Elapsed,
                runLocation: "Custom-Azure-Function",
                success:    passed,
                message:    message);
        }
    }
}

Availability Alerts

Alerts are created automatically when creating an availability test with these default settings:

  • Condition: failure from 2 or more locations out of 3 attempts.
  • Sensitivity: Medium.
  • Action: email to the test author.

Module 11 – Smart Detection

Overview

Smart Detection is a set of machine learning rules enabled by default in Application Insights, which continuously analyze telemetry data to detect anomalies.

flowchart LR
    Telemetry[Continuous\ntelemetry stream] --> ML[ML Engine\nSmart Detection]
    ML -->|Anomaly detected| Notification[Email / Alert Rule]
    ML --> Rules

    subgraph Rules
        FA[Failure Anomalies]
        PD[Performance Degradation]
        DF[Dependency Failure]
        MA[Memory Leak Detection]
        SR[Slow Page Load]
    end

Smart Detection Rules

RuleWhat it detects
Failure AnomaliesAbnormal increase in request failure rate
Performance DegradationAbnormal increase in request or dependency duration
Dependency FailureAbnormal degradation of a specific dependency
Slow Page LoadAbnormally slow page loading
Abnormal Rise in Exception VolumeAbnormal increase in exception volume
Memory Leak DetectionUpward trend in memory usage
Abnormal Rise in Daily Data VolumeAbnormally high volume of ingested data

Configuring Smart Detection Alerts

In Application Insights → Smart Detection:

  1. Each rule can be enabled / disabled individually.
  2. Configure an Action Group to receive notifications.
  3. Set the sensitivity (High / Medium / Low) based on criticality.
# Disable a rule via Azure CLI
az monitor app-insights component update \
  --app "my-app-insights" \
  --resource-group "rg-monitoring" \
  --query "properties.SmartDetectionEnabled"

# Update via ARM/Bicep
resource smartDetectionRule 'microsoft.insights/components/ProactiveDetectionConfigs@2018-05-01-preview' = {
  name: '${appInsights.name}/slowpageloadtime'
  properties: {
    enabled: false
    sendEmailsToSubscriptionOwners: false
  }
}

Smart Detection vs. Classic Alerts

CriterionSmart DetectionAzure Monitor Alerts
ConfigurationAutomatic (ML)Manual (defined threshold)
ThresholdDynamic, based on historyStatic or dynamic (separate ML)
Detection delayCan take several minutes to hoursConfigurable (1 min minimum)
Use caseUnexpected anomaliesKnown SLA violations
ResponseEmail / Action GroupFull Action Group

Exam tip: Smart Detection detects unknown anomalies; classic alerts detect known threshold violations.


Module 12 – Profiler and Snapshot Debugger

Application Insights Profiler

The Profiler collects code-level profiling traces to identify performance bottlenecks in production.

flowchart TD
    App[Production application] -->|Periodic profiling\n~2 min every hour| P[Profiler Agent]
    P -->|Upload traces| AI[Application Insights]
    AI --> PT[Profiler Traces\nCall Tree View]

    PT --> Hot[🔴 Hot Path\nLines consuming\nthe most CPU]
    PT --> Cold[🟢 Cold Path\nRarely used lines]

Enabling the Profiler

# Via the Azure portal: Application Insights → Performance → Profiler → Enable
# Via App Service:
az webapp config appsettings set \
  --name "my-api" \
  --resource-group "rg-prod" \
  --settings APPLICATIONINSIGHTS_PROFILER_ENABLED=true
// For .NET applications, the Profiler is enabled via the SDK
// appsettings.json
{
  "ApplicationInsights": {
    "ConnectionString": "...",
    "EnableAdaptiveSampling": true,
    "EnableProfiler": true
  }
}

Analyzing a Profiler Call Tree

ProcessOrder            [100%] 850ms
  ├── ValidateOrder     [ 5%]   42ms
  ├── GetCustomer       [12%]  102ms  ← SQL dependency
  ├── CalculatePricing  [71%]  603ms  🔴 HOT PATH
  │     ├── GetRules    [65%]  552ms  ← slow external HTTP call
  │     └── ApplyRules  [ 6%]   51ms
  └── SaveOrder         [12%]  103ms  ← SQL dependency
  • Flames (🔴) indicate lines consuming the most CPU.
  • Click a frame to see the exact source code.
  • Compare multiple traces to validate if the issue is systematic.

Snapshot Debugger

The Snapshot Debugger automatically captures a snapshot of the application state (local variables, stack trace) at the moment an exception occurs.

sequenceDiagram
    participant App as Application
    participant SD as Snapshot Debugger Agent
    participant AI as Application Insights

    App->>App: Exception thrown
    App->>SD: Exception intercepted
    SD->>SD: Snapshot threshold reached?
    SD->>App: In-process debugger attachment
    App->>SD: Snapshot captured (mini-dump)
    SD->>AI: Snapshot uploaded
    AI->>Dev: Notification available
    Dev->>AI: Open snapshot in VS / portal

Enabling the Snapshot Debugger

// NuGet package: Microsoft.ApplicationInsights.SnapshotCollector

// appsettings.json
{
  "SnapshotCollectorConfiguration": {
    "IsEnabled": true,
    "IsEnabledInDeveloperMode": false,
    "ThresholdForSnapshotting": 1,       // snapshots from the 1st exception
    "MaximumSnapshotsRequired": 3,        // max 3 snapshots per exception
    "MaximumCollectionPlanSize": 50,
    "ReconstructScubaPayload": false
  }
}

// Program.cs
builder.Services.AddSnapshotCollector(config =>
    builder.Configuration.Bind(nameof(SnapshotCollectorConfiguration), config));

Manually Triggering a Snapshot

using Microsoft.ApplicationInsights.SnapshotCollector;

public class OrderService
{
    private readonly ISnapshotCollector _snapshotCollector;

    public async Task ProcessAsync(Order order)
    {
        try
        {
            await ProcessOrderInternalAsync(order);
        }
        catch (Exception ex)
        {
            // Explicitly request a snapshot for this exception
            _snapshotCollector.RequestSnapshot(ex);
            throw;
        }
    }
}

Viewing a Snapshot

  1. In Application Insights → Failures → select an exception.
  2. Click Open debug snapshot.
  3. View:
    • Call stack at the time of the exception.
    • Local variables at each stack frame.
    • Method parameters in their exact state.
  4. Open in Visual Studio for a complete debugging experience.

Security: Snapshots may contain sensitive data (passwords, PII). Restrict access via RBAC. Enable the Snapshot Debugger Role for authorized users.

ComparisonProfilerSnapshot Debugger
PurposeIdentify performance bottlenecksDebug exceptions in production
Data collectedCPU call tree (sampling)Local variables + stack at exception time
Performance impactMinimal (~2% overhead)Minimal (triggered only on exception)
Retention30 days in App Insights15 days in App Insights
ActivationPortal / SDK / App Service settingsNuGet package + configuration

Module 13 – AZ-204 Review Questions

Read each question, think about the answer, then consult the explanation.


Q1. Your application emits telemetry data to Application Insights. Which TelemetryClient method should you use to record the execution time of a call to an external Redis cache?

  • A) TrackRequest()
  • B) TrackEvent()
  • C) TrackDependency()
  • D) TrackTrace()
Answer and explanation

C – TrackDependency()

TrackDependency() is designed to record outbound calls to external systems (databases, APIs, caches, etc.). TrackRequest() is reserved for inbound calls. TrackEvent() records business events. TrackTrace() records log messages.


Q2. You need to identify why a specific HTTP request takes 8 seconds to respond. Which Application Insights tool lets you visualize the Call Tree of code executed during that request?

  • A) Live Metrics Stream
  • B) Application Map
  • C) Snapshot Debugger
  • D) Application Insights Profiler
Answer and explanation

D – Application Insights Profiler

The Profiler collects CPU execution traces that let you visualize the Call Tree and identify the most time-consuming lines of code. The Snapshot Debugger captures state at exception time, not performance. The Application Map shows service topology.


Q3. You want all telemetry items emitted by your application to automatically include the TenantId field. What is the best approach?

  • A) Add TenantId manually to each TrackEvent() call
  • B) Implement an ITelemetryInitializer
  • C) Configure an ITelemetryProcessor
  • D) Use a sampling filter
Answer and explanation

B – ITelemetryInitializer

An ITelemetryInitializer is called for every telemetry item before it is sent. This is the ideal pattern for enriching all items with common properties. An ITelemetryProcessor is used to filter or modify items (e.g., sampling).


Q4. Your team receives 50 alert emails per hour due to intermittent failures of a non-critical dependency. Which Azure Monitor feature allows reducing this notification volume without disabling the alert?

  • A) Smart Detection
  • B) Alert Processing Rules (suppression)
  • C) Dynamic Thresholds
  • D) Log Analytics Saved Queries
Answer and explanation

B – Alert Processing Rules

Alert Processing Rules allow suppressing notifications during a time window, redirecting them, or applying throttling rules without modifying the alert rule itself. Smart Detection is a separate anomaly detection system.


Q5. Which KQL query returns the number of failed requests per operation for the last 6 hours?

  • A) requests | where success == false | count by name
  • B) requests | where timestamp > ago(6h) | summarize count() by name | where success == false
  • C) requests | where timestamp > ago(6h) and success == false | summarize Count = count() by name
  • D) requests | filter success = false | group by name | count
Answer and explanation

C

The correct KQL syntax uses summarize Count = count() by name for aggregation. Option A uses count by which is not valid KQL syntax. Option B filters after the summarize (too late). Option D uses incorrect SQL syntax (filter, group by).


Q6. Which HTTP header is used by the W3C TraceContext standard to propagate correlation IDs between services?

  • A) X-Request-Id
  • B) X-Correlation-Id
  • C) traceparent
  • D) X-B3-TraceId
Answer and explanation

C – traceparent

The W3C TraceContext standard (RFC 7230) defines the traceparent header (and optionally tracestate) to propagate correlation information. X-Request-Id and X-Correlation-Id are proprietary conventions. X-B3-TraceId is used by the B3 format (Zipkin).


Q7. A Standard availability test detects that your SSL certificate expires in 3 days. How does Application Insights handle this situation?

  • A) The test fails immediately with an SSL error code
  • B) The test succeeds but generates a warning
  • C) The test fails if the remaining lifetime is less than the configured value (sslCertificateRemainingLifetimeDays)
  • D) Application Insights automatically renews the certificate
Answer and explanation

C

The Standard Test lets you configure an sslCertificateRemainingLifetimeDays threshold. If the certificate expires in fewer days than this threshold, the test fails, triggering an alert. Application Insights does not have the capability to renew certificates.


Q8. Your microservices architecture has 8 services. On the Application Map, several components appear grouped under a single node. Which context property must you configure to distinguish them?

  • A) telemetry.Context.Session.Id
  • B) telemetry.Context.Cloud.RoleName
  • C) telemetry.Context.Operation.Name
  • D) telemetry.Context.User.Id
Answer and explanation

B – telemetry.Context.Cloud.RoleName

cloud_RoleName is the property used by Application Insights to distinguish components on the Application Map. Without this property, all services hosted on the same Azure resource appear merged. cloud_RoleInstance distinguishes instances of the same component.


Q9. Which of the following statements about Live Metrics Stream is correct?

  • A) Live Metrics data is stored in Log Analytics and queryable via KQL
  • B) Live Metrics requires a direct connection between the Azure portal and the application
  • C) Live Metrics displays data with ~1 second latency but does not persist it in Log Analytics
  • D) Live Metrics only works with autoinstrumentation
Answer and explanation

C

Live Metrics operates via the QuickPulse protocol with ~1 second latency. Data is ephemeral and not stored in Log Analytics. It works with the Application Insights SDK and OpenTelemetry. The connection goes through the quickpulse.applicationinsights.azure.com endpoint, not directly from the portal.


Q10. Which method is the correct approach for emitting a custom metric while minimizing the number of network calls to Application Insights?

  • A) _telemetry.TrackMetric("MyMetric", value) on each occurrence
  • B) _telemetry.GetMetric("MyMetric").TrackValue(value) to pre-aggregate on the client side
  • C) _logger.LogInformation("Metric: {Value}", value) and extract via KQL
  • D) Configure an ITelemetryProcessor to aggregate metrics
Answer and explanation

B – GetMetric().TrackValue()

GetMetric() returns a Metric object that pre-aggregates values in memory (count, sum, min, max) and only sends a periodic summary to Application Insights (by default every 60 seconds), significantly reducing transmitted data volume and ingestion costs.


Q11. You want to create an alert that triggers when your API error rate exceeds twice the normal value based on the last 7 days, without defining a fixed threshold. Which alert type should you use?

  • A) Log alert with KQL query
  • B) Metric alert with static threshold
  • C) Metric alert with Dynamic Thresholds
  • D) Smart Detection Rule
Answer and explanation

C – Metric alert with Dynamic Thresholds

Dynamic Thresholds use ML to automatically calculate thresholds based on history (7, 14, or 21 days). They adapt to seasonal patterns. Smart Detection is automatic but not configurable according to specific criteria like “twice the normal”.


Q12. When creating a Standard Availability Test, which three configuration elements can you define for the request? (Choose 3)

  • A) SSL certificate validation and minimum validity duration
  • B) HTTP verb (GET, POST, HEAD)
  • C) Request body
  • D) Response validation rules
  • E) Test timeout in milliseconds
Answer and explanation

A, B, C

For a Standard Test, you can configure:

  • A: SSL validation with minimum validity duration
  • B: HTTP verb (GET, POST, HEAD, etc.)
  • C: request body (useful for POST tests)
  • D: “Response validation rules” is not a named option – you can check the HTTP code and do a content match, but not via an option called that
  • E: the timeout is not a freely configurable parameter in Standard Test

Summary – AZ-204 Monitoring Cheat Sheet

mindmap
  root((AZ-204\nMonitoring))
    Application Insights
      SDK TelemetryClient
        TrackEvent
        TrackMetric / GetMetric
        TrackException
        TrackDependency
        TrackRequest
      OpenTelemetry Distro
      Autoinstrumentation
      Connection String
    Observability
      Logs → Log Analytics
      Traces → W3C Correlation
      Metrics → Azure Monitor
    Tools
      Live Metrics Stream
      Application Map
      Profiler
      Snapshot Debugger
      Availability Tests
      Smart Detection
    Alerts
      Metric Alerts
        Static
        Dynamic Thresholds
      Log Alerts KQL
      Activity Log Alerts
      Action Groups
    Essential KQL
      where
      project
      extend
      summarize
      join
      bin + render

Quick Reference Table

ScenarioSolution
Application goes down, don’t want to wait for usersAvailability Test + Alert
Identify which line of code is slowApplication Insights Profiler
Debug an exception in productionSnapshot Debugger
See traffic in real timeLive Metrics Stream
Understand microservices topologyApplication Map
Get alerted if error rate rises abnormallySmart Detection / Dynamic Threshold Alert
Correlate logs from multiple servicesoperation_Id (W3C TraceContext)
Avoid alert spamAlert Processing Rules (suppression)
Reduce metric ingestion costsGetMetric() pre-aggregation / Sampling
Test from multiple regions worldwideStandard Availability Test (5+ locations)

Search Terms

az-204 · monitoring · troubleshooting · applications · azure · developer · microsoft · alerts · application · insights · log · architecture · snapshot · availability · configuration · custom · detection · kql · metrics · monitor · profiler · smart · analytics · debugger

Interested in this course?

Contact us to book it or get a custom training plan for your team.