Azure Functions Debuggen met Application Insights: Van Blackbox naar Glashelder

Serverless voelt als een blackbox totdat Application Insights goed is ingericht. Praktische KQL queries, alerting en diagnostics voor Azure Functions.

Jean-Pierre Broeders

Freelance DevOps Engineer

23 maart 20267 min. leestijd
Azure Functions Debuggen met Application Insights: Van Blackbox naar Glashelder

Azure Functions Debuggen met Application Insights

Serverless is fantastisch. Tot er iets misgaat en niemand weet waar het probleem zit. Geen server logs om in te duiken, geen IIS waar even snel naar gekeken kan worden. Azure Functions draaien ergens in de cloud en als ze falen, is het zoeken naar een speld in een hooiberg — tenzij Application Insights goed staat ingesteld.

De basis: meer dan alleen aanzetten

De meeste tutorials stoppen bij "zet Application Insights aan in de portal." Dat is alsof je een rookmelder koopt en 'm in de la legt. De standaard telemetrie vangt wel wat op, maar de echte waarde zit in aangepaste configuratie.

In host.json bepaalt de logging-configuratie hoeveel data er überhaupt naar Application Insights stroomt:

{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "maxTelemetryItemsPerSecond": 20,
        "excludedTypes": "Request"
      }
    },
    "logLevel": {
      "default": "Information",
      "Host.Results": "Error",
      "Function": "Information",
      "Host.Aggregator": "Trace"
    }
  }
}

Die excludedTypes: "Request" is cruciaal. Zonder die instelling worden requests gesampled en verdwijnen er af en toe aanroepen uit de telemetrie. Bij het debuggen van sporadische fouten is dat precies de data die je nodig hebt.

KQL queries die daadwerkelijk helpen

De Log Analytics workspace achter Application Insights werkt met Kusto Query Language. Geen SQL, maar het went snel. Een paar queries die regelmatig van pas komen:

Alle mislukte function executions van de laatste 24 uur:

requests
| where timestamp > ago(24h)
| where success == false
| summarize count() by cloud_RoleName, resultCode
| order by count_ desc

Langzame executions opsporen (boven de 5 seconden):

requests
| where timestamp > ago(7d)
| where duration > 5000
| project timestamp, name, duration, resultCode
| order by duration desc
| take 50

Dependency failures — als een downstream service het laat afweten:

dependencies
| where timestamp > ago(1h)
| where success == false
| summarize failCount=count() by target, type, resultCode
| order by failCount desc

Die laatste is goud waard. Een Azure Function die een API of database aanroept kan prima draaien, maar als de dependency stilvalt, zijn het de dependency-logs die het verhaal vertellen. Niet de function zelf.

Custom telemetrie toevoegen

De standaard metrics dekken 80% van de gevallen. Voor die andere 20% is TelemetryClient nodig:

public class OrderProcessor
{
    private readonly TelemetryClient _telemetry;

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

    [Function("ProcessOrder")]
    public async Task Run(
        [QueueTrigger("orders")] OrderMessage order)
    {
        var stopwatch = Stopwatch.StartNew();

        try
        {
            await ProcessAsync(order);

            _telemetry.TrackMetric("OrderProcessingMs",
                stopwatch.ElapsedMilliseconds);

            _telemetry.TrackEvent("OrderProcessed", new Dictionary<string, string>
            {
                ["OrderId"] = order.Id,
                ["ProductCount"] = order.Items.Count.ToString()
            });
        }
        catch (Exception ex)
        {
            _telemetry.TrackException(ex, new Dictionary<string, string>
            {
                ["OrderId"] = order.Id,
                ["Stage"] = "Processing"
            });
            throw;
        }
    }
}

Die custom properties bij TrackException zijn het verschil tussen "er ging iets mis" en "order 12345 faalde tijdens de processing-stap." In een productieomgeving met honderden aanroepen per minuut scheelt dat uren zoekwerk.

Alerting: niet alles is even urgent

Een veelgemaakte fout is om op elke exception een alert te zetten. Binnen een week staat de mailbox vol en worden alle alerts genegeerd. Effectiever: gelaagde alerts.

NiveauConditieActie
KritiekFunction failure rate > 25% in 5 minSMS + PagerDuty
WaarschuwingP95 latency > 10s in 15 minTeams/Slack notificatie
InfoDagelijkse samenvatting failuresEmail digest

In de Azure Portal gaat dat via AlertsNew Alert Rule, maar de ARM template is herbruikbaar en versioneerbaar:

{
  "type": "Microsoft.Insights/metricAlerts",
  "apiVersion": "2018-03-01",
  "properties": {
    "severity": 1,
    "evaluationFrequency": "PT5M",
    "windowSize": "PT5M",
    "criteria": {
      "odata.type": "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria",
      "allOf": [
        {
          "name": "HighFailureRate",
          "metricName": "Http5xx",
          "operator": "GreaterThan",
          "threshold": 10,
          "timeAggregation": "Total"
        }
      ]
    }
  }
}

Live Metrics Stream voor real-time debugging

Soms is er een probleem dat alleen optreedt tijdens piekuren. Live Metrics Stream toont real-time wat er gebeurt: incoming requests, failures, dependency calls, alles met minder dan een seconde vertraging.

Het is geen vervanging voor KQL queries achteraf, maar voor het in real-time volgen van een deployment of het opsporen van een actief probleem is het onmisbaar. Eén tip: laat het niet de hele dag open staan. Live Metrics kost extra resources op de function app.

Distributed tracing over meerdere functions

Een queue-triggered function die weer een HTTP call maakt naar een andere function — daar wordt het zonder tracing een chaos. Application Insights koppelt automatisch een operation_id aan gerelateerde telemetrie, maar alleen als de Activity context correct doorstroomt.

requests
| where operation_Id == "abc123"
| union dependencies
| where operation_Id == "abc123"
| order by timestamp asc
| project timestamp, itemType, name, duration, success

Die query laat de hele keten zien: van de eerste trigger tot de laatste dependency call. Handig om te zien waar de vertraging precies zit.

Kosten in de gaten houden

Application Insights rekent per GB ingested data. Bij high-throughput functions loopt dat snel op. Sampling verlaagt de kosten maar vermindert ook de zichtbaarheid. Een balans vinden is nodig.

Vuistregel: sample alles behalve failures. Foutscenario's wil je altijd volledig vastleggen. Succesvolle requests mogen gesampled worden — het patroon is toch zichtbaar in de aggregaties.

Het verschil tussen een serverless setup die werkt en eentje waar vertrouwen in is? Monitoring. Niet achteraf als het al fout gaat, maar vanaf dag één als onderdeel van de architectuur.

Wil je op de hoogte blijven?

Schrijf je in voor mijn nieuwsbrief of neem contact op voor freelance projecten.

Neem Contact Op