Monitoring and Observability

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

See the section bibliography for the reference material consulted while preparing these pages.

Commands on this page are current for Azure CLI 2.90.0 (checked 2026-09-27); re-verify against az version and the linked reference pages before relying on them in a pipeline. az monitor app-insights component ships in the application-insights extension, which the CLI installs automatically the first time it is invoked.

Every other page in this section shows how to build one piece of Bookshelf — cae-bookshelf, aks-bookshelf, afd-bookshelf, pg-bookshelf, and the rest. This page is the one that answers, once all of that exists, "is it healthy, and what happened at 03:14 this morning": Azure Monitor as the platform that collects metrics and logs from every one of those resources, a Log Analytics workspace as the place logs land and KQL as the language that queries them, Application Insights as the application-level view of catalog-api, orders-api and notifier, and alerts, workbooks, Service Health, Advisor and Network Watcher as the ways that data turns into something a human or an on-call rotation actually acts on.

Azure Monitor: one platform, two kinds of data

Azure Monitor is not a separate resource to deploy — every Azure resource emits into it by default. It collects two fundamentally different kinds of telemetry:

Metrics

Lightweight, numeric, time-stamped values (CPU percentage, request count, replica count) stored in a time-series database that Azure retains and indexes automatically. Platform metrics exist the moment a resource exists, need no configuration, and are cheap enough to evaluate every minute — which is why metric alerts (below) are the fast, near-real-time alerting path.

Logs

Structured or free-text records (a request trace, a container’s stdout, a resource’s activity log entry) that only reach Azure Monitor once something is configured to send them there — most commonly a diagnostic setting pointing at a Log Analytics workspace. Logs are richer and queryable with KQL, but land with some delay (typically a few minutes) and cost per gigabyte ingested and retained.

A Log Analytics workspace is the Log Analytics query engine plus the storage for every log table sent to it. Bookshelf uses one workspace, law-bookshelf, as the shared destination for `cae-bookshelf’s and `aks-bookshelf’s container logs, `afd-bookshelf’s access logs, and the Application Insights component below — consolidating them lets a single KQL query join a request across every layer it passed through.

In the Azure portal

  1. Search for Log Analytics workspaces and select Create.

  2. On the Basics tab, choose rg-bookshelf, name the workspace law-bookshelf, and pick the region (eastus2 in Bookshelf’s examples).

  3. Leave the default pricing tier (Pay-as-you-go) and retention (30 days) unless a longer retention or a commitment tier is already justified by volume.

  4. Select Review + create, then Create.

With the Azure CLI

az monitor log-analytics workspace create \
  --resource-group "$RG" \
  --workspace-name law-bookshelf \
  --location "$LOCATION"

KQL: querying Bookshelf’s logs

Kusto Query Language (KQL) reads like a pipeline: a table name, then |-separated operators that filter, project, summarize or join, each acting on the output of the one before it. Every example below can be run from Logs on law-bookshelf itself, or from the Logs blade of the specific resource (which pre-scopes the query to that resource).

Container Apps

Where `cae-bookshelf’s console output (`catalog-api’s and `orders-api’s stdout/stderr) and system logs (revision changes, scaling decisions) end up depends on the environment’s logs destination, and each destination writes to differently named tables with different columns:

log-analytics

The environment streams logs straight to one workspace, with no diagnostic setting involved. They land in the custom-log tables ContainerAppConsoleLogs_CL and ContainerAppSystemLogs_CL, whose columns carry type suffixes (ContainerAppName_s, Log_s).

azure-monitor

Logs are routed by diagnostic settings (below) to a workspace, a storage account, or an event hub. In a workspace they land in the resource-specific tables ContainerAppConsoleLogs and ContainerAppSystemLogs, whose columns have no suffix (ContainerAppName, Log).

none

Nothing is stored; only live log streaming works.

Bookshelf routes every resource through diagnostic settings, so cae-bookshelf uses azure-monitor:

az containerapp env update \
  --name cae-bookshelf \
  --resource-group "$RG" \
  --logs-destination azure-monitor

With that destination and the diagnostic setting below in place, `catalog-api’s recent errors are:

ContainerAppConsoleLogs
| where ContainerAppName == "catalog-api"
| where TimeGenerated > ago(1h)
| where Log contains "ERROR"
| project TimeGenerated, RevisionName, ContainerGroupName, Log
| order by TimeGenerated desc

On an environment using the log-analytics destination instead, the same query reads ContainerAppConsoleLogs_CL and uses the suffixed columns (ContainerAppName_s, RevisionName_s, Log_s).

AKS

aks-bookshelf’s Container insights (enabled via `az aks enable-addons -a monitoring, covered on AKS Clusters) writes pod stdout/stderr to ContainerLogV2 and pod/node inventory to KubePodInventory:

ContainerLogV2
| where PodNamespace == "bookshelf"
| where PodName startswith "notifier"
| where LogMessage has "exception"
| project TimeGenerated, PodName, ContainerName, LogMessage
| order by TimeGenerated desc
KubePodInventory
| where ClusterName == "aks-bookshelf"
| summarize RestartCount = max(PodRestartCount) by Name, Namespace
| where RestartCount > 0
| order by RestartCount desc

Front Door

afd-bookshelf (Front Door Standard/Premium) sends its access, health probe, and WAF logs to the AzureDiagnostics table, told apart by Category (FrontDoorAccessLog, FrontDoorHealthProbeLog, FrontDoorWebApplicationFirewallLog). Fields are flattened into type-suffixed columns, and the status code arrives as a string (httpStatusCode_s), so it’s converted before comparing:

AzureDiagnostics
| where Category == "FrontDoorAccessLog"
| where TimeGenerated > ago(24h)
| where toint(httpStatusCode_s) >= 500
| summarize ErrorCount = count() by requestUri_s
| order by ErrorCount desc

Diagnostic settings: wiring a resource to the workspace

A diagnostic setting is the explicit link between one resource’s logs/metrics and one or more destinations (a Log Analytics workspace, an event hub, or a storage account for long-term archive). Without one, a resource’s logs are never sent anywhere durable — platform metrics keep flowing regardless, but resource-log tables like ContainerAppConsoleLogs or AzureDiagnostics stay empty. For cae-bookshelf specifically, the diagnostic setting only takes effect once the environment’s logs destination is azure-monitor (see Container Apps).

In the Azure portal

  1. Open the resource (cae-bookshelf, aks-bookshelf, or afd-bookshelf) and select Diagnostic settings under Monitoring.

  2. Select Add diagnostic setting, name it (e.g. send-to-law-bookshelf).

  3. Under Logs, select the categories to collect (all logs, or a specific category such as ContainerAppConsoleLogs).

  4. Under Destination details, check Send to Log Analytics workspace and pick law-bookshelf.

  5. Select Save.

With the Azure CLI

WORKSPACE_ID=$(az monitor log-analytics workspace show \
  --resource-group "$RG" --workspace-name law-bookshelf --query id -o tsv)

CAE_ID=$(az containerapp env show \
  --resource-group "$RG" --name cae-bookshelf --query id -o tsv)

az monitor diagnostic-settings create \
  --name send-to-law-bookshelf \
  --resource "$CAE_ID" \
  --workspace "$WORKSPACE_ID" \
  --logs '[{"category":"ContainerAppConsoleLogs","enabled":true},{"category":"ContainerAppSystemLogs","enabled":true}]' \
  --metrics '[{"category":"AllMetrics","enabled":true}]'

The same command, pointed at `aks-bookshelf’s or `afd-bookshelf’s resource ID with that resource’s own log category names, wires up the AKS and Front Door queries above.

Application Insights: the application-level view

Application Insights is Azure Monitor’s application performance monitoring (APM) feature: request rates, response times, dependency calls (catalog-api calling pg-bookshelf, orders-api calling docdb-bookshelf), exceptions, and distributed traces that follow one request across catalog-api, orders-api and notifier. A workspace-based Application Insights component — the only kind the portal creates today — stores its data as extra tables inside a Log Analytics workspace (law-bookshelf), so KQL queries can join AppRequests, AppExceptions and AppDependencies with the container and platform logs above in one query.

Connection strings, not instrumentation keys

Every Application Insights component exposes a connection string — an ingestion endpoint plus an instrumentation key, e.g. InstrumentationKey=<app-insights-connection-string> — that an SDK or agent uses to send telemetry. Support for ingesting telemetry using a bare instrumentation key (with no connection string) ended on 2025-03-31; instrumentation-key-only configuration may still work but receives no further updates, so every new integration should be configured with the full connection string from day one.

The Azure Monitor OpenTelemetry Distro

The Azure Monitor OpenTelemetry Distro is Microsoft’s current recommendation for instrumenting new applications — a Microsoft-maintained OpenTelemetry distribution that auto-collects traces, metrics, logs and exceptions and exports them to Application Insights, rather than the older, now-legacy Application Insights SDKs. For catalog-api and orders-api (Spring Boot), the distro is added as the Spring Cloud Azure Starter Monitor dependency alongside the OpenTelemetry BOM:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.opentelemetry</groupId>
      <artifactId>opentelemetry-bom</artifactId>
      <version>1.55.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependency>
  <groupId>com.azure.spring</groupId>
  <artifactId>spring-cloud-azure-starter-monitor</artifactId>
</dependency>

No application code changes are required beyond the dependency; the connection string is then supplied as an environment variable, never in code or a checked-in properties file:

APPLICATIONINSIGHTS_CONNECTION_STRING=$(az monitor app-insights component show \
  --app appi-bookshelf --resource-group "$RG" --query connectionString -o tsv)

On cae-bookshelf, that variable is set as a Container App secret-backed environment variable (covered on Container Apps); on aks-bookshelf, as a Kubernetes secret mounted into catalog-api’s/`orders-api’s pod spec (covered on Runtime Configuration) — in neither case does the connection string need to be a long-lived secret pulled from `kv-bookshelf, since it identifies a telemetry destination rather than granting access to anything sensitive, but treating it as configuration rather than a literal in source control still avoids leaking one Application Insights component’s data into another environment’s.

In the Azure portal

  1. Search for Application Insights and select Create.

  2. Choose rg-bookshelf, name it appi-bookshelf, and under Resource Mode leave Workspace-based selected.

  3. Under Log Analytics Workspace, pick law-bookshelf.

  4. Select Review + create, then Create, then open the resource’s Overview pane and copy the Connection String.

With the Azure CLI

az monitor app-insights component create \
  --app appi-bookshelf \
  --resource-group "$RG" \
  --location "$LOCATION" \
  --workspace "$WORKSPACE_ID" \
  --application-type web \
  --kind web

Alerts and action groups

An action group is a reusable notification target — one or more email, SMS, voice, webhook, Azure Function, Logic App or ITSM receivers, defined once and referenced by many alert rules. Bookshelf defines one, ag-bookshelf-oncall, with an email receiver for the on-call rotation:

az monitor action-group create \
  --name ag-bookshelf-oncall \
  --resource-group "$RG" \
  --short-name bookshelf \
  --action email oncall oncall@example.com

A metric alert evaluates a metric-based condition on a fixed schedule (near-real-time, typically every one to five minutes) and fires the action group when it is breached. This one watches catalog-api’s replica count on `cae-bookshelf:

CATALOG_API_ID=$(az containerapp show \
  --resource-group "$RG" --name catalog-api --query id -o tsv)

AG_ID=$(az monitor action-group show \
  --resource-group "$RG" --name ag-bookshelf-oncall --query id -o tsv)

az monitor metrics alert create \
  --name catalog-api-no-replicas \
  --resource-group "$RG" \
  --scopes "$CATALOG_API_ID" \
  --condition "avg Replicas < 1" \
  --window-size 5m \
  --evaluation-frequency 1m \
  --severity 1 \
  --action "$AG_ID" \
  --description "catalog-api has scaled to zero unexpectedly"

A log search alert is the log-data equivalent: instead of a built-in metric condition, it runs a KQL query on a schedule and fires when the result set crosses a threshold — the natural way to alert on one of the KQL queries above (e.g. "more than 10 ERROR lines from catalog-api in the last 5 minutes"). It is created with az monitor scheduled-query create rather than metrics alert create, since it targets a Log Analytics workspace and a query rather than a metric namespace.

Workbooks and dashboards

A workbook is an interactive report — text, KQL query results (tables, charts), and parameters the reader can change — authored once in the portal and reused across incidents, distinct from a one-off ad hoc query. Azure Monitor ships gallery templates for Container Apps, AKS and Application Insights that already chart request rate, failure rate, latency and dependency health; cloning one of those and adding a Bookshelf-specific KQL query (such as the Front Door error-count query above) is the usual starting point, rather than authoring a workbook from a blank canvas. A dashboard is the simpler, older alternative: a fixed grid of tiles (metric charts, a workbook, a static markdown tile) pinned from individual resource blades, better suited to an always-on big-screen view than to interactive investigation. Both are ARM resources under the hood and can be defined as Bicep/ARM templates for repeatable deployment rather than only clicked together in the portal, which is the practical way to keep a workbook’s definition in source control alongside the rest of Bookshelf’s infrastructure-as-code (covered on Infrastructure as Code).

Service Health, Advisor, and Network Watcher

These three sit next to Azure Monitor rather than inside it — each answers a different "is something wrong" question that resource-level metrics and logs cannot:

Azure Service Health

Reports incidents and planned maintenance on the Azure platform itself — an outage affecting Container Apps in eastus2, for instance — scoped to the resources actually deployed, with alerting of its own so a platform incident is known before Bookshelf’s own alerts start firing for the symptom.

Azure Advisor

Continuously analyzes configuration and usage across a subscription and recommends changes across five categories (Reliability, Security, Performance, Cost, Operational Excellence) — for example, flagging that pg-bookshelf has no configured high-availability standby, or that stbookshelf allows public network access it does not use. Advisor’s cost recommendations are covered in more depth on Cost Management.

Network Watcher

A regional suite of network diagnostic tools — IP flow verify, next hop, connection troubleshoot, packet capture, and NSG/topology visualization — for the case metrics and logs narrow a problem down to "something between vnet-bookshelf and a resource isn’t connecting" but not which hop. It complements rather than replaces Virtual Networks's NSG flow logs.

How it fits together

flowchart LR CAE["cae-bookshelf /\naks-bookshelf /\nafd-bookshelf"] -- "diagnostic setting" --> LAW[("law-bookshelf\n(Log Analytics workspace)")] APPI["appi-bookshelf\n(Application Insights,\nworkspace-based)"] --> LAW LAW --> KQL["KQL queries\n(Logs blade)"] LAW --> WB["Workbooks / dashboards"] CAE -- "platform metrics" --> METRICALERT["Metric alerts"] LAW -- "log search alerts" --> LSA["Scheduled query alerts"] METRICALERT --> AG["Action group\nag-bookshelf-oncall"] LSA --> AG AG --> ONCALL["On-call\n(email / SMS / webhook)"]

Clean up

az group delete --name "$RG" --yes --no-wait

This removes law-bookshelf, appi-bookshelf, ag-bookshelf-oncall, and every alert rule and diagnostic setting created on this page, along with the rest of Bookshelf’s resources in $RG. Application Insights and Log Analytics workspaces have no soft-delete purge step of their own to worry about (unlike Key Vault or API Management), so deleting the resource group is sufficient.

  • Container Apps and AKS Clusters cover the resources whose logs feed the KQL examples above.

  • Front Door and CDN covers the access and WAF logs queried in the Front Door example.

  • Runtime Configuration covers how the Application Insights connection string reaches a running Container App or AKS pod as configuration.

  • Cost Management covers Advisor’s cost recommendations and Log Analytics ingestion pricing in more depth.

  • Virtual Networks covers NSG flow logs, which Network Watcher’s tools complement.

  • Infrastructure as Code covers deploying workbooks and alert rules as Bicep/ARM templates instead of clicking them together in the portal.