Back to Blog
7 min read

Azure Monitor Application Insights: Deep Observability for Applications

Application Insights is Azure’s application performance monitoring (APM) service. At Ignite 2021, Microsoft announced workspace-based Application Insights GA and new features for enhanced observability.

What is Application Insights?

Application Insights provides:

  • Automatic instrumentation: Minimal code changes
  • Distributed tracing: End-to-end request tracking
  • Live metrics: Real-time performance data
  • Smart detection: AI-powered anomaly detection
  • Application Map: Visualize dependencies

Setting Up Application Insights

.NET 6 Integration

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

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

// Configure telemetry
builder.Services.ConfigureTelemetryModule<DependencyTrackingTelemetryModule>((module, options) =>
{
    module.EnableSqlCommandTextInstrumentation = true;
});

// Add custom telemetry initializer
builder.Services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();

var app = builder.Build();
app.Run();

Custom telemetry initializer:

public class CustomTelemetryInitializer : ITelemetryInitializer
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CustomTelemetryInitializer(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        var context = _httpContextAccessor.HttpContext;
        if (context == null) return;

        // Add custom properties
        if (telemetry is ISupportProperties propTelemetry)
        {
            // Add user information
            if (context.User.Identity?.IsAuthenticated == true)
            {
                propTelemetry.Properties["UserId"] = context.User.FindFirst("sub")?.Value ?? "unknown";
            }

            // Add request correlation
            propTelemetry.Properties["CorrelationId"] =
                context.Request.Headers["X-Correlation-ID"].FirstOrDefault()
                ?? context.TraceIdentifier;

            // Add environment
            propTelemetry.Properties["Environment"] =
                Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
        }
    }
}

Node.js Integration

// app.js
const appInsights = require("applicationinsights");

appInsights.setup(process.env.APPLICATIONINSIGHTS_CONNECTION_STRING)
    .setAutoDependencyCorrelation(true)
    .setAutoCollectRequests(true)
    .setAutoCollectPerformance(true, true)
    .setAutoCollectExceptions(true)
    .setAutoCollectDependencies(true)
    .setAutoCollectConsole(true, true)
    .setUseDiskRetryCaching(true)
    .setSendLiveMetrics(true)
    .setDistributedTracingMode(appInsights.DistributedTracingModes.AI_AND_W3C)
    .start();

const client = appInsights.defaultClient;

// Custom telemetry
client.trackEvent({
    name: "OrderCreated",
    properties: {
        orderId: "12345",
        customerId: "C123",
        totalAmount: 99.99
    }
});

client.trackMetric({
    name: "OrderProcessingTime",
    value: 1500
});

// Custom dependency tracking
const startTime = Date.now();
try {
    // External call
    const result = await externalService.call();

    client.trackDependency({
        target: "external-service",
        name: "GetData",
        data: "https://external.api/data",
        duration: Date.now() - startTime,
        resultCode: 200,
        success: true,
        dependencyTypeName: "HTTP"
    });
} catch (error) {
    client.trackDependency({
        target: "external-service",
        name: "GetData",
        duration: Date.now() - startTime,
        resultCode: 500,
        success: false,
        dependencyTypeName: "HTTP"
    });
    throw error;
}

Python Integration

# app.py
from opencensus.ext.azure import metrics_exporter
from opencensus.ext.azure.trace_exporter import AzureExporter
from opencensus.ext.flask.flask_middleware import FlaskMiddleware
from opencensus.trace.samplers import ProbabilitySampler
import logging

# Configure logging
logger = logging.getLogger(__name__)
handler = AzureLogHandler(
    connection_string=os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING']
)
logger.addHandler(handler)
logger.setLevel(logging.INFO)

# Configure Flask middleware
app = Flask(__name__)
middleware = FlaskMiddleware(
    app,
    exporter=AzureExporter(connection_string=os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING']),
    sampler=ProbabilitySampler(rate=1.0)
)

# Configure metrics
exporter = metrics_exporter.new_metrics_exporter(
    connection_string=os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING']
)

# Custom metrics
from opencensus.stats import aggregation, measure, stats, view

order_count_measure = measure.MeasureInt("order_count", "Number of orders", "orders")
order_count_view = view.View(
    "order_count",
    "Count of orders processed",
    [],
    order_count_measure,
    aggregation.CountAggregation()
)

stats.stats.view_manager.register_view(order_count_view)
mmap = stats.stats.stats_recorder.new_measurement_map()

@app.route('/orders', methods=['POST'])
def create_order():
    # Track custom event
    logger.info("Order created", extra={
        'custom_dimensions': {
            'order_id': order.id,
            'customer_id': order.customer_id
        }
    })

    # Record metric
    mmap.measure_int_put(order_count_measure, 1)
    mmap.record()

    return jsonify(order)

Distributed Tracing

W3C Trace Context

// Propagate trace context in outgoing calls
public class TracingHttpHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var activity = Activity.Current;
        if (activity != null)
        {
            // Add W3C traceparent header
            request.Headers.Add("traceparent",
                $"00-{activity.TraceId}-{activity.SpanId}-{(activity.Recorded ? "01" : "00")}");

            // Add tracestate if present
            if (!string.IsNullOrEmpty(activity.TraceStateString))
            {
                request.Headers.Add("tracestate", activity.TraceStateString);
            }
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

// Register handler
builder.Services.AddHttpClient("api")
    .AddHttpMessageHandler<TracingHttpHandler>();

Custom Operations

public class OrderProcessor
{
    private readonly TelemetryClient _telemetry;

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

    public async Task<Order> ProcessOrderAsync(OrderRequest request)
    {
        // Start operation
        using var operation = _telemetry.StartOperation<RequestTelemetry>("ProcessOrder");
        operation.Telemetry.Properties["OrderId"] = request.OrderId;

        try
        {
            // Track dependencies
            using (_telemetry.StartOperation<DependencyTelemetry>("ValidateInventory"))
            {
                await ValidateInventoryAsync(request);
            }

            using (_telemetry.StartOperation<DependencyTelemetry>("ProcessPayment"))
            {
                await ProcessPaymentAsync(request);
            }

            // Track custom event
            _telemetry.TrackEvent("OrderProcessed", new Dictionary<string, string>
            {
                { "OrderId", request.OrderId },
                { "TotalAmount", request.TotalAmount.ToString() }
            });

            operation.Telemetry.Success = true;
            return new Order { Id = request.OrderId, Status = "Completed" };
        }
        catch (Exception ex)
        {
            operation.Telemetry.Success = false;
            _telemetry.TrackException(ex, new Dictionary<string, string>
            {
                { "OrderId", request.OrderId }
            });
            throw;
        }
    }
}

KQL Queries for Application Insights

Performance Analysis

// Slow requests
requests
| where timestamp > ago(1h)
| where duration > 1000
| project timestamp, name, duration, resultCode, operation_Id
| order by duration desc
| take 100

// Request performance percentiles
requests
| where timestamp > ago(24h)
| summarize
    Count = count(),
    Avg = avg(duration),
    P50 = percentile(duration, 50),
    P90 = percentile(duration, 90),
    P95 = percentile(duration, 95),
    P99 = percentile(duration, 99)
    by bin(timestamp, 1h), name
| render timechart

// Failed requests by error
requests
| where timestamp > ago(24h)
| where success == false
| summarize Count = count() by resultCode, name
| order by Count desc

// Dependency performance
dependencies
| where timestamp > ago(1h)
| summarize
    AvgDuration = avg(duration),
    FailureRate = countif(success == false) * 100.0 / count()
    by target, name
| order by AvgDuration desc

User Analytics

// Active users
pageViews
| where timestamp > ago(7d)
| summarize Users = dcount(user_Id) by bin(timestamp, 1d)
| render timechart

// User sessions by location
sessions
| where timestamp > ago(24h)
| summarize SessionCount = count() by client_CountryOrRegion
| order by SessionCount desc
| take 20

// User flow through pages
pageViews
| where timestamp > ago(24h)
| project timestamp, user_Id, name
| order by timestamp asc
| serialize
| extend PrevPage = prev(name, 1), PrevUser = prev(user_Id, 1)
| where user_Id == PrevUser
| summarize Transitions = count() by PrevPage, name
| where Transitions > 10

Exception Analysis

// Top exceptions
exceptions
| where timestamp > ago(24h)
| summarize Count = count() by type, outerMessage
| order by Count desc
| take 20

// Exception trends
exceptions
| where timestamp > ago(7d)
| summarize Count = count() by bin(timestamp, 1h), type
| render timechart

// Exception with full stack trace
exceptions
| where timestamp > ago(1h)
| project timestamp, type, outerMessage, details
| take 10

Alerts and Smart Detection

Create Alert Rules

param appInsightsName string
param actionGroupId string

resource appInsights 'Microsoft.Insights/components@2020-02-02' existing = {
  name: appInsightsName
}

resource failureRateAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
  name: 'High Failure Rate'
  location: 'global'
  properties: {
    description: 'Alert when request failure rate exceeds 5%'
    severity: 2
    enabled: true
    scopes: [appInsights.id]
    evaluationFrequency: 'PT1M'
    windowSize: 'PT5M'
    criteria: {
      'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
      allOf: [
        {
          name: 'FailureRate'
          metricName: 'requests/failed'
          operator: 'GreaterThan'
          threshold: 5
          timeAggregation: 'Average'
          criterionType: 'StaticThresholdCriterion'
        }
      ]
    }
    actions: [
      {
        actionGroupId: actionGroupId
      }
    ]
  }
}

resource responseTimeAlert 'Microsoft.Insights/scheduledQueryRules@2021-08-01' = {
  name: 'Slow Response Time'
  location: resourceGroup().location
  properties: {
    description: 'Alert when P95 response time exceeds 2 seconds'
    severity: 3
    enabled: true
    evaluationFrequency: 'PT5M'
    windowSize: 'PT15M'
    scopes: [appInsights.id]
    criteria: {
      allOf: [
        {
          query: '''
            requests
            | summarize P95 = percentile(duration, 95) by bin(timestamp, 5m)
            | where P95 > 2000
          '''
          timeAggregation: 'Count'
          operator: 'GreaterThan'
          threshold: 0
        }
      ]
    }
    actions: {
      actionGroups: [actionGroupId]
    }
  }
}

Workbooks and Dashboards

Custom Workbook

{
  "version": "Notebook/1.0",
  "items": [
    {
      "type": 1,
      "content": {
        "json": "## Application Health Dashboard"
      }
    },
    {
      "type": 3,
      "content": {
        "version": "KqlItem/1.0",
        "query": "requests\n| where timestamp > ago(24h)\n| summarize Requests = count(), Failures = countif(success == false), AvgDuration = avg(duration) by bin(timestamp, 1h)\n| project timestamp, Requests, Failures, AvgDuration",
        "size": 0,
        "queryType": 0,
        "resourceType": "microsoft.insights/components",
        "visualization": "timechart"
      }
    },
    {
      "type": 3,
      "content": {
        "version": "KqlItem/1.0",
        "query": "dependencies\n| where timestamp > ago(24h)\n| summarize AvgDuration = avg(duration), FailureRate = countif(success == false) * 100.0 / count() by target\n| order by AvgDuration desc",
        "size": 0,
        "queryType": 0,
        "resourceType": "microsoft.insights/components",
        "visualization": "table"
      }
    }
  ]
}

Application Insights provides the deep observability needed for modern applications. Combined with Azure Monitor’s broader capabilities, it enables proactive monitoring and rapid incident response.

Resources

Michael John Pena

Michael John Pena

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.