Infrastructure

Azure Monitor : diagnostiquer la latence d’ingestion avant de modifier une alerte KQL

Un runbook de production pour qualifier une alerte Azure Monitor qui arrive trop tard en séparant horodatage applicatif, ingestion Log Analytics, requête KQL, fenêtre d'évaluation, action group, validation et rollback.

14 juil. 2026 azureazure-monitorlog-analyticskqlobservabilityalertsingestionautomationrunbookrollbackproduction

Une alerte KQL qui arrive quinze minutes après le début d’un incident pousse souvent à corriger la mauvaise couche. On élargit la fenêtre, on baisse un seuil, on ajoute une fréquence d’évaluation plus agressive, puis on découvre que les logs arrivent déjà en retard dans Log Analytics. Le problème n’était pas la règle d’alerte. C’était le délai entre l’événement réel, son horodatage applicatif, son ingestion, son apparition dans la table et l’exécution de la règle.

Le cas d’usage est une application Azure supervisée avec Application Insights, Log Analytics, Diagnostic Settings, Data Collection Rules et alertes KQL. Après un changement de collecte, un déploiement, une montée de volume ou une rationalisation de coût, les alertes continuent d’exister mais se déclenchent trop tard. Le but du runbook est de décider si l’on doit corriger la collecte, ajuster temporairement la fenêtre KQL, rollbacker un changement d’observabilité ou accepter un délai documenté pour un signal non critique.

Nommer le délai que l’on mesure

Commence par séparer trois horloges. TimeGenerated décrit le moment de l’événement tel que le producteur le voit. ingestion_time() décrit, quand disponible, le moment où la ligne devient interrogeable côté Log Analytics. L’heure de déclenchement de l’alerte décrit le moment où Azure Monitor a évalué la requête et envoyé l’action.

text alert-latency-contract.txt
Signal a qualifier
Alerte: api-orders-errors-prod
Table: AppExceptions ou AppRequests
Ressource source: app-orders-prod
Symptôme utilisateur: erreurs HTTP 500 sur checkout
Heure du premier symptôme observé: 2026-07-14T07:42:00Z
Heure de première ligne visible dans Log Analytics
Heure de déclenchement de l'alerte
Fenêtre KQL: 5m, 15m ou 30m
Fréquence d'évaluation: 1m, 5m ou 10m
Action group: incident, astreinte, automation ou ticket

Questions avant modification
Les logs sont-ils produits en retard ou ingérés en retard ?
La table concernée est-elle la seule touchée ?
La règle lit-elle une fenêtre plus courte que la latence observée ?
Le problème est-il constant ou limité à un pic de volume ?
Un changement de DCR, Diagnostic Setting ou sampling vient-il d'avoir lieu ?

Sans cette distinction, l’équipe risque de transformer une alerte en filet trop large. Une fenêtre plus longue peut cacher la latence, mais elle peut aussi regrouper plusieurs incidents et retarder la sortie d’alerte.

Mesurer la latence dans la table touchée

La première requête mesure l’écart entre le temps de l’événement et le temps d’ingestion. Elle doit être exécutée sur la table utilisée par l’alerte, pas seulement sur une table générale qui semble saine.

kusto 01-measure-ingestion-latency.kql
let Window = 6h;
let Service = "orders-api";
AppExceptions
| where TimeGenerated > ago(Window)
| where AppRoleName == Service
| extend IngestedAt = ingestion_time()
| extend IngestionDelay = IngestedAt - TimeGenerated
| summarize
  rows = count(),
  p50 = percentile(IngestionDelay, 50),
  p95 = percentile(IngestionDelay, 95),
  p99 = percentile(IngestionDelay, 99),
  maxDelay = max(IngestionDelay)
by bin(TimeGenerated, 15m)
| order by TimeGenerated asc

Si ingestion_time() n’est pas exploitable sur la table, compare le premier moment où un événement attendu apparaît avec l’heure applicative contenue dans le payload ou dans une dimension contrôlée. L’important est de ne pas confondre absence d’erreur et absence de preuve.

Comparer tables, ressources et pipeline de collecte

Une latence isolée sur une table applicative ne se traite pas comme une latence généralisée sur le workspace. Compare les tables critiques : requêtes, exceptions, traces, logs plateforme, WAF, Key Vault, Service Bus ou VM selon le périmètre.

kusto 02-compare-tables-latency.kql
let Window = 6h;
union withsource=TableName
(AppRequests | where TimeGenerated > ago(Window) | project TimeGenerated, IngestedAt=ingestion_time()),
(AppExceptions | where TimeGenerated > ago(Window) | project TimeGenerated, IngestedAt=ingestion_time()),
(AppTraces | where TimeGenerated > ago(Window) | project TimeGenerated, IngestedAt=ingestion_time()),
(AzureDiagnostics | where TimeGenerated > ago(Window) | project TimeGenerated, IngestedAt=ingestion_time())
| extend IngestionDelay = IngestedAt - TimeGenerated
| summarize rows=count(), p95=percentile(IngestionDelay, 95), maxDelay=max(IngestionDelay) by TableName, bin(TimeGenerated, 30m)
| order by TimeGenerated asc, TableName asc

Si toutes les tables dérivent, regarde le workspace, le volume et le chemin d’ingestion. Si une seule table dérive, cherche plutôt le SDK, le sampling, la transformation DCR, la catégorie Diagnostic Settings ou le producteur.

Vérifier collecte, sampling et transformations

Un changement de collecte peut ralentir ou filtrer le signal sans casser totalement l’ingestion. La bonne preuve est la configuration exacte au moment de l’incident : Diagnostic Settings, DCR, destination, transformations, sampling applicatif et agent.

bash 03-capture-collection-state.sh
RESOURCE_ID="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-prod/providers/Microsoft.Web/sites/app-orders-prod"
WORKSPACE_ID="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-obs/providers/Microsoft.OperationalInsights/workspaces/log-prod"

az monitor diagnostic-settings list --resource "$RESOURCE_ID" --query "[].{name:name,workspace:workspaceId,logs:logs,metrics:metrics}" --output json

az monitor data-collection rule list --resource-group rg-obs --query "[].{name:name,location:location,streams:properties.dataFlows[].streams,destinations:properties.destinations}" --output json

az monitor log-analytics workspace show --ids "$WORKSPACE_ID" --query "{name:name,retention:retentionInDays,sku:sku.name,provisioningState:provisioningState}" --output json

Capture aussi les changements applicatifs : version du SDK, niveau de logs, sampling adaptatif, volume de traces et éventuel enrichissement coûteux. Une alerte peut être juste, mais alimentée par un pipeline devenu trop lent.

Relire la règle d’alerte comme un contrat temporel

Une alerte KQL est un contrat entre la fenêtre de requête, la fréquence d’évaluation, la logique de seuil et l’action group. Si la latence p95 dépasse la fenêtre de requête, l’alerte peut rater le signal ou le déclencher après coup.

bash 04-read-alert-rule.sh
RG="rg-obs-prod"
RULE="api-orders-errors-prod"

az monitor scheduled-query show --resource-group "$RG" --name "$RULE" --query "{enabled:enabled,scopes:scopes,evaluationFrequency:evaluationFrequency,windowSize:windowSize,severity:severity,criteria:criteria,actions:actions}" --output json

Ne modifie pas la règle avant d’avoir prouvé que le signal arrive trop tard. Si la collecte est lente, un ajustement de fenêtre peut être un contournement temporaire, pas la correction durable.

Décider correction, contournement ou rollback

La décision doit être écrite avec le niveau de risque. Une alerte utilisateur critique ne tolère pas la même latence qu’un rapport quotidien de capacité.

yaml alert-latency-decision.yml
decision:
correct_collection:
  when:
    - latency_increased_after_dcr_or_diagnostic_change
    - only_one_table_or_resource_is_delayed
    - alert_window_was_valid_before_change
  actions:
    - restore_previous_collection_configuration
    - remove_filter_or_transformation_that_delays_signal
    - validate_sampling_and_sdk_version

temporary_alert_adjustment:
  when:
    - ingestion_latency_is_known_and_bounded
    - signal_is_not_user_critical_or_has_secondary_alert
    - rollback_collection_is_not_immediate
  actions:
    - increase_window_size_with_expiry
    - document_expected_detection_delay
    - keep_user_symptom_alert_active

rollback_observability_change:
  when:
    - critical_alert_misses_incident_window
    - latency_source_is_recent_change
    - no_equivalent_signal_exists
  actions:
    - restore_previous_dcr_or_diagnostic_setting
    - revert_sampling_change
    - re-run alert replay query

block_rule_change:
  when:
    - no_ingestion_evidence
    - missing_table_or_ambiguous_time_source
    - action_group_delay_not_separated_from_log_delay

Le piège classique consiste à compenser une latence non maîtrisée par une fenêtre toujours plus large. Cela peut calmer l’incident du jour, mais l’équipe ne sait plus si elle détecte un problème en cours ou un problème déjà terminé.

Rejouer l’alerte avant de fermer l’incident

Après correction, rejoue la logique de l’alerte sur une fenêtre historique qui contient le symptôme. La validation doit prouver que le signal aurait été détecté dans un délai acceptable.

kusto 05-replay-alert-window.kql
let IncidentStart = datetime(2026-07-14T07:42:00Z);
let IncidentEnd = datetime(2026-07-14T08:10:00Z);
let DetectionWindow = 5m;
AppExceptions
| where TimeGenerated between (IncidentStart .. IncidentEnd)
| where AppRoleName == "orders-api"
| summarize errors=count() by bin(TimeGenerated, DetectionWindow)
| where errors >= 5
| order by TimeGenerated asc

Compare ensuite avec la latence d’ingestion mesurée après correction. Si le signal arrive encore après la fenêtre de décision, la règle n’est pas validée même si elle finit par passer au vert dans le portail.

Conclusion

Une alerte Azure Monitor en retard n’est pas seulement un problème de seuil. C’est un problème de chaîne temporelle : production du log, collecte, ingestion, requête, évaluation et notification. Changer la règle trop tôt revient à régler une montre sans savoir laquelle retarde.

Le bon runbook mesure la latence dans la table réellement utilisée, compare les pipelines, capture les configurations de collecte, relit la règle comme un contrat temporel et choisit entre correction, contournement temporaire ou rollback. La décision finale doit dire clairement quel délai de détection reste acceptable et quelle preuve permet de le surveiller.