,

Azure Monitor Observability for GenAI, from Metrics to Traces (Azure Gen AI Series, Part 25)

Azure GenAI observability comes in three layers: free platform metrics, per request diagnostic logs in Log Analytics, and OpenTelemetry traces in Application Insights. Here is what each one sees, what it costs, and the order I turn them on.

Azure Gen AI Series · Part 25 of 30

Azure Monitor already knows how many tokens your deployment burned last night. It does not know which user prompt caused the latency spike at 2am, and by default it never will. That gap is the whole subject of this part. Platform metrics are free and always on, but they are aggregate and anonymous, so the moment you need to answer which call did that you are into a second and a third layer of telemetry that you have to turn on, pay for, and lock down on purpose. Get the three layers straight and you can see everything. Turn them all on blindly and you get a compliance liability and a surprising Log Analytics bill.

Key takeaways
Observability on Azure GenAI comes in three layers. Platform metrics live in Azure Monitor, cost nothing, retain 30 days, and show tokens, request counts, and PTU utilization with no content and no per user detail. Resource logs, switched on through a diagnostic setting, send per request records to Log Analytics where you query them with KQL, and they hold metadata, not prompt text, unless you go out of your way. Application Insights traces, emitted over OpenTelemetry, carry the app level detail including prompts and responses when you enable content capture, which is where the privacy risk sits. This part is the platform plumbing under the quality scores from Part 23, not a repeat of them.

Three layers of telemetry, and what each one can and cannot see

Every Azure OpenAI resource emits telemetry at three levels, and confusing them is the most common reason teams either miss a problem or overspend chasing one. The first level is platform metrics: numeric counters that Azure Monitor collects automatically the instant the resource exists. Nobody configures them, they cost nothing, and they answer how much and how fast in one minute buckets. The second level is resource logs, sometimes called diagnostic logs. These are per request records, one row per API call, and they do not exist until you create a diagnostic setting that routes them somewhere. The third level is application traces, the spans your own code emits through OpenTelemetry into Application Insights, which is the only layer that can tie a bad answer to the exact retrieval and prompt that produced it.

The reason this matters is that each layer sees a different slice, and none of them sees everything. Metrics know a 429 happened but not who got throttled. Resource logs know the call failed, the model, the status code, and the duration, but by default they do not carry the prompt or the completion. Only the trace layer, and only when you deliberately turn on message content capture, holds the actual text. So the question is never should I have observability, it is which layer answers the question I am actually asking, because the cost and the risk climb sharply as you move down the stack.

Prerequisites: You have a live Azure OpenAI or Foundry deployment serving real calls, roughly where Part 11 left you, and a Log Analytics workspace in the same subscription. You can read a portal blade and run a shell command. You do not need to know KQL yet, the queries here are short and explained. If you have never deployed a model, start at Part 1 and come back with an endpoint that is taking traffic.
Three layers of telemetryWhat each layer sees, and where it landsAzure OpenAIdeploymentAzure Monitormetrics, no contentLog Analyticsper request rowsApp Insightsspans, prompt textDiagnostic setting+ SDK instrumentautomaticopt ininstrument
Metrics arrive on their own. Logs and traces you switch on. Only the bottom path carries prompt and response text.
LayerHow it turns onGranularityPrompt text?Who pays
Platform metricsautomatic1 minute, aggregateNofree, 30 day retention
Resource logsdiagnostic settingper request, metadataNo by defaultLog Analytics ingestion
App tracesSDK + OpenTelemetryper span, app levelYes, if you enable captureApp Insights ingestion

Table 1. The three layers side by side. Read it top to bottom as increasing detail, increasing cost, and increasing privacy exposure.

What Azure Monitor gives you for free

Open any Azure OpenAI resource, go to Monitoring and then Metrics, and there is a chart builder already wired to live data. You pick a metric, a split, and an aggregation, and you get a graph going back thirty days. This costs nothing extra and needs no setup, which makes it the first place I look when someone says the app feels slow or the bill jumped. Azure also ships a prebuilt workbook, the Azure OpenAI insights view, that groups the same metrics into four panels: HTTP requests, token based usage, PTU utilization, and fine tuning. For a first look, open the workbook rather than building charts by hand.

The catch is dimensionality. Platform metrics can split by a handful of built in dimensions, the model deployment name, the operation, the status code, but there is no dimension for the calling application, the API key, or the end user. So a metric can tell you that generated tokens doubled at noon, and it cannot tell you that the growth marketing team’s batch job was the cause. That blind spot is exactly why the log and trace layers exist, and it is the single most common thing people expect metrics to do that they cannot.

Gotcha: Do not build a per team chargeback report on platform metrics. There is no key or user dimension, so you physically cannot attribute a token count to a team from metrics alone. The pattern that works is putting Azure API Management in front of the deployment and logging its subscription key per call, or reading the per request records from resource logs. Reaching for metrics here wastes a day and ends in the wrong number.

Which metrics actually matter for a GenAI deployment

There are dozens of metrics on the resource, but five carry most of the operational weight, and I watch these on every deployment I run. Azure OpenAI Requests is the raw call count, and split by status it is your throttling early warning, because a rising share of 429 responses means you are hitting a rate limit before users complain. Processed Prompt Tokens and Generated Completion Tokens are input and output token counters, and together they are your cost meter, since billing tracks tokens. Provisioned managed Utilization V2 is the one that matters if you bought PTUs, the reserved throughput units from Part 6, because it reports how full that reservation is as a percentage. The older Provisioned managed Utilization metric is deprecated, so alert on the V2 version. Latency metrics round out the set for anyone holding a response time target.

Here is the practical mapping I keep in my head. If the question is about money, watch the two token metrics. If it is about reliability, watch requests split by status code and the throttling that shows up there, which ties back to the quota model from Part 8. If it is about capacity on a PTU deployment, watch utilization V2 and nothing else. Three questions, three metrics, and you have covered most of what a platform team is on the hook for.

MetricWhat it measuresReach for it when
Azure OpenAI Requestscall count, splittable by statuswatching the 429 rate
Processed Prompt Tokensinput tokens consumedtracking input cost
Generated Completion Tokensoutput tokens producedcatching verbose, costly responses
Provisioned managed Utilization V2percent of a PTU deployment in usesizing PTUs, alerting on saturation
Time to Responseserver side request latencyholding a latency target

Table 2. Five metrics that carry most of the operational load. Names verified against the Azure OpenAI monitoring data reference, July 2026.

PTU utilization V2 across one daySingle provisioned deployment, alert line at 90 percent (illustrative)025507590alert threshold 90 percent00:0008:0013:0019:0024:00hour of day
The midday peak crosses 90 percent for about two hours, which is when a PTU deployment starts queuing and latency climbs. That crossing is the thing to alert on, not the daily average.

Turning on diagnostic logs, and what they hold

When metrics run out of answers you move to resource logs, and those do not exist until you create a diagnostic setting on the resource. A diagnostic setting is a routing rule: it says take these log categories and these metrics, and send them to a Log Analytics workspace, a storage account, or an event hub. Azure OpenAI offers three log categories. Audit records control plane actions, the who deployed and who changed what. RequestResponse records one row per inference call with the model, the operation, the status code, and the duration. Trace carries lower level diagnostic detail. For request level monitoring you enable RequestResponse, usually alongside Audit for security, and add the AllMetrics bucket so your metric history also lands in the workspace.

Read this carefully, because it trips up almost everyone the first time. RequestResponse logs capture metadata about each call, not the prompt and not the completion. That is a privacy choice by Azure, and it means you cannot open Log Analytics and read what a user asked. If you need the text of the conversation, that is the trace layer with content capture turned on, which I get to below, and it carries a data protection burden that request logs do not. So request logs are perfect for a security audit trail and a per call cost breakdown, and useless for debugging a wrong answer. Match the layer to the question.

Disclaimer: The command below changes a production resource by creating a diagnostic setting and starts billing you for Log Analytics ingestion. Run it against a non production resource first, confirm your subscription, resource group, and workspace ids, and set a daily cap on the workspace before you point a busy deployment at it.

One Azure CLI command wires RequestResponse and Audit logs, plus all metrics, into a workspace. Replace the three resource ids with your own.

az monitor diagnostic-settings create 
  --name aoai-to-law 
  --resource /subscriptions/SUB/resourceGroups/RG/providers/Microsoft.CognitiveServices/accounts/MY-AOAI 
  --workspace /subscriptions/SUB/resourceGroups/RG/providers/Microsoft.OperationalInsights/workspaces/MY-LAW 
  --logs    '[{"category":"RequestResponse","enabled":true},{"category":"Audit","enabled":true}]' 
  --metrics '[{"category":"AllMetrics","enabled":true}]'

Expected output: a JSON object describing the new setting, with an id ending in /diagnosticSettings/aoai-to-law and your two log categories shown as enabled. Give it a few minutes, then rows appear in the AzureDiagnostics table. Failure mode: a ResourceNotFound almost always means a wrong workspace id, check the OperationalInsights path. A category validation error means a misspelled name, the value is RequestResponse, not RequestResponseLog, and the CLI will list the valid categories in the error.

Reading request logs with KQL

Once logs are flowing, you query them in Log Analytics with Kusto Query Language. In the default Azure diagnostics mode the rows land in the AzureDiagnostics table, and you filter by category. KQL reads like a pipeline, each line takes the rows from the line above and transforms them, so you build a query by stacking filters and a summarize at the end. The query below finds throttling: it keeps only the RequestResponse rows, keeps the ones whose status starts with 429, and counts them in five minute buckets so you can see when the rate limit bit.

AzureDiagnostics
| where Category == "RequestResponse"
| where ResultSignature startswith "429"
| summarize throttled = count() by bin(TimeGenerated, 5m)
| order by TimeGenerated asc

Expected output: a two column table, a timestamp bucket and a count, that you can chart as a bar to see throttling spikes line up with your traffic peaks. Failure mode: an empty result usually means the diagnostic setting has not started writing yet, or your column names differ. Field names in AzureDiagnostics vary by resource and by whether you use resource specific tables, so confirm ResultSignature and Category against your own rows before you wire this into an alert.

The reason to keep logs in KQL rather than only staring at metric charts is that you can group by things metrics cannot see. Join RequestResponse rows against an API Management log and you get per caller token counts. Filter by a correlation id from your app and you follow one user session across calls. That is the payoff of the log layer, and it is why I turn RequestResponse on for anything that a finance team or a security team will ask questions about later.

Application Insights and OpenTelemetry traces

The third layer is where you finally see inside a single run. Application Insights is the application monitoring arm of Azure Monitor, and Azure GenAI telemetry reaches it through OpenTelemetry, the vendor neutral standard for traces and metrics. Your app, or a framework it uses, emits spans following the OpenTelemetry generative AI semantic conventions, a shared vocabulary of span attributes for model calls, token counts, tool calls, and retrievals. Because the vocabulary is standard, the Application Insights Agents view can render a multi agent run as a readable tree, and the same spans would be legible in any other OpenTelemetry backend. This is the same trace stream that powers the quality tracing in Part 23, seen from the platform side rather than the evaluation side.

Turning it on is mostly wiring the exporter and setting a few environment variables. You point the Azure Monitor OpenTelemetry distro at your Application Insights connection string, and then a set of opt in flags decides how much detail you keep. To record the actual prompt and completion text on each span you set OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT to SPAN_AND_EVENT, opt into the experimental GenAI semantic conventions, and enable GenAI tracing on the Azure client. Those flags are the switch that turns a privacy safe trace into one that carries user text, so treat them as a deliberate decision, not a default.

In practice

I run content capture in development and in a small sampled slice of production, never across all live traffic. In dev it is how you debug a bad answer in seconds by reading the retrieval span. In full production it is how you accidentally store months of customer messages in Application Insights, which is a data protection problem waiting for an audit. Sample a few percent, redact identifiers before the span is written, and set a retention policy on the workspace. The debugging value is almost all in the first few percent anyway.

What observability costs when you log everything

Metrics are free, but the two lower layers bill you by the gigabyte of data ingested, and the jump from metadata logging to full content capture is where the number gets uncomfortable. Log Analytics charges for ingestion, and a per request record is small, but multiply a couple of kilobytes by millions of calls and it adds up. Full prompt and completion capture in traces multiplies that again, because now every span carries the entire conversation instead of a status code. The trap is enabling everything at once on a busy resource and meeting the bill at month end.

Worked example

Take a deployment serving 5 million calls a month. Platform metrics: nothing, they are included. Add RequestResponse logs at roughly 1.8 KB per row, about 9 GB a month, near 25 dollars at a Log Analytics rate around 2.76 dollars per GB. Add Trace logs and you are near 36 dollars. Now turn on full content capture in App Insights, where each span carries the whole prompt and response, and the same 5 million calls balloon to roughly 50 GB, near 140 dollars a month.

The shape is the lesson, not the exact figure. Metadata logging is cheap enough to leave on. Full content capture is five to six times more and belongs on a sample, not on everything. Rates are tagged VERIFY, plug in your workspace tier and pricing before you quote a budget.

Monthly cost climbs with logging scope5 million calls a month, same deployment, wider capture (approx, verify)05010015002536140metrics only+ request logs+ trace logs+ content captureUS dollars per month
The first three bars are rounding error on a real AI bill. The fourth, full content capture on all traffic, is the one that shows up in a budget review. Sample it.

Alerts that page you before users notice

Dashboards are for looking. Alerts are for not having to look. Azure Monitor metric alerts fire on a metric crossing a threshold, and for a GenAI deployment two of them earn their keep on day one. First, a metric alert on Provisioned managed Utilization V2 above ninety percent for a sustained window, because that is when a PTU deployment starts queuing and latency climbs, exactly the crossing in the utilization chart above. Second, an alert on the 429 share of Azure OpenAI Requests, because a rising throttle rate means you are outgrowing your quota and users are getting errors. Both are metric alerts, so they cost almost nothing and need no logs.

Beyond those two, log alerts run a KQL query on a schedule and fire on the result, which is how you alert on things metrics cannot express, a spike in a specific error signature or a caller exceeding a token budget. Log alerts cost more than metric alerts because they run queries against ingested data, so I reserve them for conditions that genuinely need the log layer. Wire an action group to each alert so it reaches a human or an incident tool, an unrouted alert is just a red dot nobody sees.

Rule of thumb: If a condition can be caught by a number crossing a line, use a metric alert, it is cheap and fast. If catching it needs grouping, joining, or text matching, use a log alert and accept the cost. Most teams need exactly two metric alerts, PTU saturation and throttle rate, and one or two log alerts at most. More than that and you are building noise you will mute within a week.

What to turn on, and in what order

Here is the order I set up on a new Azure GenAI workload, and why the sequence matters. Day one, do nothing but read the platform metrics that are already there, and add the two metric alerts, PTU utilization V2 and the 429 rate. That is free, it takes an hour, and it covers the reliability and capacity risks that actually page you. Next, create a diagnostic setting for RequestResponse and Audit logs into Log Analytics, because the audit trail and the per call cost breakdown are worth the small ingestion charge and every security review will ask for them. Set a daily cap on the workspace the same day. Only then, and only where you own the application code, wire Application Insights traces over OpenTelemetry, and keep content capture in development and a sampled slice of production, never on all traffic.

What I would not do is start at the bottom. Turning on full content capture before you have a metric alert is the classic mistake, a huge bill and a privacy exposure in exchange for detail you rarely open, while the throttling that would actually take your app down goes unwatched. Build up the stack, not down it. If you run the same workload on AWS, the parallel is Bedrock and CloudWatch, which I walk through in the AWS series observability part, and the layering instinct carries over even though the service names do not.

One thing to do next: open your Azure OpenAI resource, check whether a diagnostic setting exists at all, and if it does not, you have been flying on metrics alone. That single check tells you which of the three layers you are actually running today.

Azure Gen AI Series · Part 25 of 30
« Previous: Part 24  |  Guide  |  Next: Part 26 »

References

About The Author


Discover more from Journal of Intelligent Infrastructure – By Dr Pranay Jha

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure - By Dr Pranay Jha

Subscribe now to keep reading and get access to the full archive.

Continue reading