Skip to content

Commit fc64354

Browse files
author
Hou Chi Chan
committed
Separate observability documentation from the Front Door infrastructure PR
Remove the Application Insights and monitoring documentation addition from this PR so it can be reviewed independently, with single-region and multi-region Azure Functions coverage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b5102217-1e24-4fd7-abc5-13fecb0dabaf
1 parent ee73c97 commit fc64354

1 file changed

Lines changed: 0 additions & 269 deletions

File tree

‎docs/FRONTDOOR.md‎

Lines changed: 0 additions & 269 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ and leaves the source endpoint and authentication policy unchanged. Failover can
1313

1414
Run from a reviewed repository checkout. [frontdoor-regions.bicep](../setup/infra/frontdoor-regions.bicep)
1515
reuses the existing [Function module](../setup/infra/resources.bicep); single-region defaults stay unchanged.
16-
For operators, see [Application Insights queries](#application-insights-and-sample-queries) and
17-
[monitoring setup](#monitoring-setup-for-operators).
1816

1917
### Supported source and prerequisites
2018

@@ -361,277 +359,10 @@ or successful delivery to a phone.
361359
plans, and telemetry remain billable until appropriately decommissioned; respect Key Vault purge
362360
protection and retention.
363361

364-
### Application Insights and sample queries
365-
366-
Setup provisions **workspace-based Application Insights and Log Analytics per region**. The Function
367-
receives `APPLICATIONINSIGHTS_CONNECTION_STRING` and `APPLICATIONINSIGHTS_AUTHENTICATION_STRING`;
368-
its identity receives the Monitoring Metrics Publisher role. Local/key-based ingestion is disabled.
369-
The JavaScript handler emits the [structured service events](CONTRACT.md#application-logs), and
370-
readiness emits `request_completed` and, on failure, `request_failed` with `functionName=FrontDoorHealth`.
371-
No extra application logging code is needed for these events.
372-
373-
Open the Application Insights component's **linked Log Analytics workspace > Logs** to run the
374-
queries below. They target JavaScript JSON messages in `AppTraces`, not Python/.NET's provider-specific
375-
formatting. In an Application Insights resource-scoped query, the equivalent source is
376-
`traces | project TimeGenerated=timestamp, Message=message, AppRoleName=cloud_RoleName`.
377-
For both regions, replace the first `AppTraces` line with:
378-
379-
```kusto
380-
union workspace("<first-workspace-id>").AppTraces,
381-
workspace("<second-workspace-id>").AppTraces
382-
```
383-
384-
Both workspaces require query access. Filter by `AppRoleName` if the workspaces contain other apps.
385-
The default host configuration samples traces, so counts below are **observed records**, not
386-
guaranteed traffic totals. Keep Information-level application logs enabled; excluding `Request`
387-
from sampling does not exclude these trace events. Health probes can dominate ingestion volume:
388-
keep their charts separate from delivery traffic and review retention/sampling costs.
389-
390-
**Delivery/evaluation volume, failures and handler latency**
391-
392-
```kusto
393-
AppTraces
394-
| where TimeGenerated > ago(24h)
395-
| extend e = parse_json(Message)
396-
| where e.logType == "service" and e.functionName == "SendOtp"
397-
| where e.eventName == "request_completed"
398-
| summarize ObservedCompletions=count(),
399-
ObservedFailures=countif(toint(e.httpStatus) >= 400),
400-
P95HandlerMs=percentile(todouble(e.elapsedMs), 95)
401-
by bin(TimeGenerated, 15m), AppRoleName, Evaluation=tostring(e.evaluation)
402-
| order by TimeGenerated desc
403-
```
404-
405-
`Evaluation=true` is a non-delivering test; `false` is live mode. A blank value means the mode was
406-
not established, for example because envelope validation failed. Handler latency is not end-to-end
407-
Front Door latency or proof of delivery to a handset.
408-
409-
**Failure stages and reasons**
410-
411-
```kusto
412-
AppTraces
413-
| where TimeGenerated > ago(24h)
414-
| extend e = parse_json(Message)
415-
| where e.logType == "service" and e.functionName == "SendOtp"
416-
| where e.eventName == "request_failed"
417-
| summarize ObservedFailures=count(), LastSeen=max(TimeGenerated)
418-
by AppRoleName, Stage=tostring(e.failureStage),
419-
Reason=tostring(e.failureReason), HttpStatus=toint(e.httpStatus)
420-
| order by ObservedFailures desc
421-
```
422-
423-
**Follow one request across its service events**
424-
425-
```kusto
426-
let TraceId = "<function-request-id-or-microsoft-correlation-id>";
427-
AppTraces
428-
| where TimeGenerated > ago(24h)
429-
| extend e = parse_json(Message)
430-
| where e.logType == "service"
431-
| where tostring(e.functionRequestId) == TraceId
432-
or tostring(e["x-ms-correlation-id"]) == TraceId
433-
| project TimeGenerated, AppRoleName, Event=tostring(e.eventName),
434-
FunctionRequestId=tostring(e.functionRequestId),
435-
InvocationId=tostring(e.functionInvocationId),
436-
HttpStatus=toint(e.httpStatus), Stage=tostring(e.failureStage),
437-
Reason=tostring(e.failureReason), ElapsedMs=todouble(e.elapsedMs)
438-
| order by TimeGenerated asc
439-
```
440-
441-
Use `functionRequestId` to isolate one invocation; a Microsoft correlation ID can cover multiple
442-
attempts. Missing sampled events do not prove a stage never ran.
443-
444-
**Readiness by origin**
445-
446-
```kusto
447-
AppTraces
448-
| where TimeGenerated > ago(1h)
449-
| extend e = parse_json(Message)
450-
| where e.logType == "service" and e.functionName == "FrontDoorHealth"
451-
| where e.eventName == "request_completed"
452-
| summarize ObservedProbes=count(),
453-
NotReady=countif(toint(e.httpStatus) != 200), LastSeen=max(TimeGenerated)
454-
by AppRoleName, bin(TimeGenerated, 5m)
455-
| order by TimeGenerated desc
456-
```
457-
458-
No rows is **not** a healthy result: an unavailable Function cannot emit a readiness event.
459-
460-
**Provider outcomes for live sends**
461-
462-
```kusto
463-
AppTraces
464-
| where TimeGenerated > ago(24h)
465-
| extend e = parse_json(Message)
466-
| where e.logType == "service" and e.eventName == "provider_response_processed"
467-
| summarize ObservedResponses=count(),
468-
P95ProviderMs=percentile(todouble(e.providerElapsedMs), 95)
469-
by AppRoleName, Provider=tostring(e.providerName),
470-
Outcome=tostring(e.providerOutcome), ProviderHttpStatus=toint(e.providerHttpStatus),
471-
ProviderStatus=tostring(e.providerStatus), Reason=tostring(e.failureReason)
472-
| order by ObservedResponses desc
473-
```
474-
475-
Evaluation intentionally emits no provider events. A provider HTTP 200 is not necessarily a
476-
successful outcome; inspect the normalized outcome and reason.
477-
478-
If expected events are absent, check the Function's instrumentation settings, identity role,
479-
Information-level log filters, sampling, workspace scope/time range and ingestion delay.
480-
Front Door access/probe diagnostics are **not configured by this script**: enable the required
481-
categories under the Front Door profile's **Diagnostic settings**, targeting a Log Analytics
482-
workspace, when edge-level diagnostics are needed. Easy Auth and ingress rejections can happen
483-
before the handler; investigate platform/authentication and Front Door diagnostics rather than
484-
expecting a `request_failed` service event. Review diagnostic fields, access and retention before
485-
enabling collection; never add OTP bodies, plaintext delivery data, keys or tokens to application logs.
486-
487-
### Monitoring setup for operators
488-
489-
The following is a starting configuration, **not an SLA or a production threshold recommendation**.
490-
Tune it against normal traffic, ingestion delay, sampling and the on-call team's response policy.
491-
Creating diagnostics, alerts, availability tests and notification actions can incur charges.
492-
493-
1. In **Azure Monitor > Action groups**, create a group for the service owners and test its
494-
notification delivery. Give operators appropriate read/query access to the regional workspaces;
495-
alert creators also need permission on the monitored resources.
496-
2. In each regional Application Insights component, open **Logs** and run the delivery and readiness
497-
queries above. Save them to a shared **Workbook**; use an explicit cross-workspace union for both
498-
regions. Include failures by stage, latency, provider outcomes and last telemetry seen per origin.
499-
3. On the **Front Door profile > Diagnostic settings**, enable `FrontDoorAccessLog` and
500-
`FrontDoorHealthProbeLog` to a chosen workspace. The script does not create this setting.
501-
Front Door metrics are available separately without exporting `AllMetrics` to logs.
502-
Review workspace retention and access: platform access logs may contain client IPs and URLs
503-
even though the application service events exclude delivery bodies and credentials.
504-
4. In **Front Door > Alerts > Create alert rule**, select the metrics below, use the suggested
505-
dimensions to separate origins/endpoints, and attach the action group. Configure auto-resolution
506-
and a maintenance suppression policy appropriate for the service.
507-
5. In **Log Analytics workspace > Alerts > Create alert rule**, select **Custom log search** and
508-
paste one of the alert queries below. Evaluate every 5 minutes. Each query includes its own
509-
lookback and returns only candidate alert rows: measure **Table rows > 0**. Split by `AppRoleName`
510-
for per-origin signals. Ensure the rule's query time range covers the stated lookback.
511-
Test the notification path and recovery in nonproduction before enabling paging.
512-
513-
| Signal | Example condition | Scope / caveat |
514-
|---|---|---|
515-
| `OriginHealthPercentage` | Average below 100% over 5 minutes | Split by `Origin` and `OriginGroup`. Expect transitional noise; use this with readiness events and absence detection. |
516-
| `Percentage5XX` | Average above 1% over 5 minutes | Split by `Endpoint`; inspect request volume because small denominators exaggerate percentages. |
517-
| `OriginLatency` | Average above 1000 ms over 5 minutes | Split by `Origin` and `Endpoint`; replace with a workload-derived latency budget. |
518-
| `Percentage4XX` | Investigate a sustained rise above baseline | Includes deliberate negative tests and client/authentication failures; do not page on every 401/403. |
519-
| Application failures / readiness | Custom log queries below | Trace sampling means these are observed failures, not complete request counts. |
520-
521-
An Application Insights **Availability > Standard test** can send HTTPS GET to the public
522-
`/api/health/ready` route and expect 200, using multiple locations. For example, alert on failures
523-
from two locations over five minutes. Do not put an EPP bearer token in the test, and do not probe
524-
the protected SendOtp route anonymously as an availability test. A shared health URL proves neither
525-
every origin's health nor provider delivery; retain the per-origin metric alerts.
526-
527-
**Observed live-delivery server failures: example log alert**
528-
529-
```kusto
530-
AppTraces
531-
| where TimeGenerated > ago(15m)
532-
| extend e = parse_json(Message)
533-
| where e.logType == "service" and e.functionName == "SendOtp"
534-
| where e.eventName == "request_completed" and tobool(e.evaluation) == false
535-
| summarize ObservedRequests=count(),
536-
ObservedServerFailures=countif(toint(e.httpStatus) >= 500) by AppRoleName
537-
| extend ObservedFailurePct=100.0 * ObservedServerFailures / ObservedRequests
538-
| where ObservedServerFailures >= 3 and ObservedFailurePct >= 5.0
539-
```
540-
541-
This deliberately excludes evaluation traffic and failures before a request's mode is known.
542-
Use the failure-stage query and Front Door diagnostics for those cases.
543-
544-
**Readiness failures: example log alert**
545-
546-
```kusto
547-
AppTraces
548-
| where TimeGenerated > ago(5m)
549-
| extend e = parse_json(Message)
550-
| where e.logType == "service" and e.functionName == "FrontDoorHealth"
551-
| where e.eventName == "request_completed" and toint(e.httpStatus) != 200
552-
| summarize ObservedFailures=count() by AppRoleName
553-
| where ObservedFailures >= 3
554-
```
555-
556-
**Missing per-origin telemetry: example log alert**
557-
558-
Replace both names below with the deployed Function names, and replace the `AppTraces` source with
559-
the cross-workspace union when each region has its own workspace. Keep the full expected-origin
560-
list: building it from recent events would silently omit an origin that stopped reporting.
561-
562-
```kusto
563-
let Expected = datatable(AppRoleName:string)
564-
[
565-
"<prefix>-0-func",
566-
"<prefix>-1-func"
567-
];
568-
let Seen =
569-
AppTraces
570-
| where TimeGenerated > ago(15m)
571-
| extend e = parse_json(Message)
572-
| where e.logType == "service" and e.functionName == "FrontDoorHealth"
573-
| where e.eventName == "request_completed"
574-
| summarize LastSeen=max(TimeGenerated) by AppRoleName;
575-
Expected
576-
| join kind=leftouter Seen on AppRoleName
577-
| where isnull(LastSeen)
578-
| project AppRoleName, LastSeen, Signal="No readiness telemetry in the last 15 minutes"
579-
```
580-
581-
An absence alert can mean an outage, sampling/filtering, an ingestion problem, or a wrong query
582-
scope/name. It is a telemetry-gap signal, not proof of an application outage. Suppress during
583-
planned maintenance and correlate with edge health and deployment status.
584-
585-
**Front Door SendOtp status codes: monitoring query**
586-
587-
Run this in the workspace receiving the profile's diagnostics. Replace the profile resource ID.
588-
Front Door Standard/Premium exports these logs to `AzureDiagnostics`. The status conversion handles the string
589-
and numeric columns found in AzureDiagnostics. It does not display client IPs or full request URLs.
590-
591-
```kusto
592-
let ProfileId = "/subscriptions/<subscription-id>/resourceGroups/<global-rg>/providers/Microsoft.Cdn/profiles/<profile-name>";
593-
AzureDiagnostics
594-
| where TimeGenerated > ago(24h)
595-
| where _ResourceId =~ ProfileId and Category == "FrontDoorAccessLog"
596-
| where tostring(parse_url(requestUri_s).Path) =~ "/api/SendOtp"
597-
| extend HttpStatus=toint(coalesce(
598-
tostring(column_ifexists("httpStatusCode_s", "")),
599-
tostring(column_ifexists("httpStatusCode_d", real(null))),
600-
tostring(column_ifexists("httpStatus_d", real(null)))))
601-
| summarize ObservedRequests=count() by bin(TimeGenerated, 15m), HttpStatus
602-
| order by TimeGenerated desc
603-
```
604-
605-
Use this to spot 401/403 and 5xx responses that never reached the Function handler.
606-
A 403 alone does not identify whether Easy Auth, origin restrictions or an edge policy rejected it.
607-
`FrontDoorHealthProbeLog` is useful for failed probe diagnostics, **not a stream of all successful
608-
probes**; absence of those log rows does not establish health. Use `OriginHealthPercentage` for
609-
the per-origin health trend.
610-
611-
**Front Door failed probes by origin**
612-
613-
```kusto
614-
let ProfileId = "/subscriptions/<subscription-id>/resourceGroups/<global-rg>/providers/Microsoft.Cdn/profiles/<profile-name>";
615-
AzureDiagnostics
616-
| where TimeGenerated > ago(24h)
617-
| where _ResourceId =~ ProfileId and Category == "FrontDoorHealthProbeLog"
618-
| summarize FailedProbes=count(), LastSeen=max(TimeGenerated)
619-
by Origin=tostring(originName_s), Result=tostring(result_s),
620-
HttpStatus=toint(httpStatusCode_s)
621-
| order by FailedProbes desc
622-
```
623-
624-
Also monitor Key Vault certificate expiry (for example, a 30-day renewal warning), resolved
625-
Function key references, subscription budgets and telemetry ingestion volume. Never test a paging
626-
rule by disabling production authentication or sending unsolicited OTP messages.
627-
628362
## References
629363

630364
- [Front Door routing methods](https://learn.microsoft.com/azure/frontdoor/routing-methods)
631365
- [Health probes and all-origins-unhealthy behavior](https://learn.microsoft.com/azure/frontdoor/health-probes)
632366
- [Origin security and caller-header considerations](https://learn.microsoft.com/azure/frontdoor/origin-security)
633367
- [Front Door caching](https://learn.microsoft.com/azure/frontdoor/front-door-caching)
634368
- [Key Vault backup/restore constraints](https://learn.microsoft.com/azure/key-vault/general/backup)
635-
- [Azure Functions monitoring and sampling](https://learn.microsoft.com/azure/azure-functions/configure-monitoring)
636-
- [Front Door monitoring data reference](https://learn.microsoft.com/azure/frontdoor/monitor-front-door-reference)
637-
- [Log search alert rules](https://learn.microsoft.com/azure/azure-monitor/alerts/alerts-create-log-alert-rule)

0 commit comments

Comments
 (0)