Study guide

12.3 Write KQL Queries to Analyze Logs

Exam AI-200: Developing AI Cloud Solutions on Azure Exam

Track your progress as you read

Finished sections get a check mark.

1 Introduction & Exam Overview

1.1 Current Exam Facts 1.2 Audience Profile & Study Path

2 Chapter 1: Container Registry & App Service Hosting

1.1 ACR: Build, Store, Version & Manage Images 1.2 ACR Tasks: Build & Run Images 1.3 Deploy Containers to Azure App Service

3 Chapter 2: Azure Container Apps & KEDA Scaling

2.1 Azure Container Apps: Environments, Config & Revisions 2.2 KEDA Event-Driven Scaling in Container Apps

4 Chapter 3: AKS Manifests & Container Troubleshooting

3.1 Deploy & Manage Apps on AKS with Manifests 3.2 Monitor & Troubleshoot AKS and Container Apps

5 Chapter 4: Cosmos DB for NoSQL Fundamentals

4.1 Cosmos DB for NoSQL: SDK Connect & Queries 4.2 Optimize RUs: Indexing Policies & Consistency Levels

6 Chapter 5: Cosmos DB Vectors & Change Feed

5.1 Embeddings & Vector Similarity Search in Cosmos DB 5.2 Change Feed Processor for New/Updated Items

7 Chapter 6: Azure PostgreSQL Foundations for AI

6.1 Azure Database for PostgreSQL: Connect & Query with SDKs 6.2 Schema Design, Data Types & Indexing Strategies 6.3 pgvector Indexing: Latency & Compute Overhead

8 Chapter 7: PostgreSQL Vector Workloads & RAG

7.1 Configure Compute, Memory & Storage for Vector Workloads 7.2 Vector Search, Semantic Retrieval & RAG with Metadata Filters 7.3 Connection Optimization for Throughput & Latency

9 Chapter 8: Azure Managed Redis for AI Solutions

8.1 Azure Managed Redis: Caching, Expiration & Invalidation 8.2 Redis Vector Indexing for Similarity Search

10 Chapter 9: Service Bus & Event Grid for AI Pipelines

9.1 Service Bus Queues & Message Processing 9.2 Topics, Subscriptions & Dead-Letter Queues 9.3 Event Grid: Custom Events & Filters 9.4 Event Grid Retries & Event-Driven Workflows

11 Chapter 10: Azure Functions for Serverless AI Back Ends

10.1 Azure Functions: Triggers & Bindings 10.2 Build Serverless APIs with Functions 10.3 Configure & Deploy Function Apps

12 Chapter 11: Key Vault & App Configuration

11.1 Azure Key Vault: Secret Storage & Retrieval 11.2 Key Vault Secret Rotation Patterns 11.3 Azure App Configuration for App Settings

13 Chapter 12: OpenTelemetry & KQL Observability

12.1 Distributed Tracing with OpenTelemetry SDKs 12.2 Correlating Traces into Azure Monitoring 12.3 Write KQL Queries to Analyze Logs 12.4 KQL for Metrics & Troubleshooting Scenarios

12.3 Write KQL Queries to Analyze Logs

Key Takeaways Explain This Topic AI

KQL (Kusto Query Language) is the query language for Log Analytics—AI-200 expects practical operators for investigating AI solution logs.

Core operators include where, project, summarize, join, and ago-based time filters on TimeGenerated.

Application Insights tables such as AppRequests, AppTraces, AppExceptions, and AppDependencies are first-stop sources for correlated app logs.

Container Apps and custom logs often appear in tables like ContainerAppConsoleLogs_CL—know when to query platform logs vs App* tables.

Start from time range + failed requests, then join traces on operation_Id to reconstruct the narrative.

Last updated: July 2026

KQL in the AI-200 monitoring toolkit

After OpenTelemetry data lands in Azure Monitor, you investigate with Kusto Query Language (KQL) in a Log Analytics workspace . AI-200 does not require you to memorize every function, but you must write and interpret queries that filter time, select columns, aggregate, and join related tables.

Mental model: table → filter → shape → aggregate → join

Most troubleshooting queries follow the same pipeline:

  • Pick a table ( AppRequests , AppTraces , …)
  • where to filter time and predicates
  • project / extend to keep useful columns
  • summarize to aggregate
  • join when one table is not enough
  • Operator Purpose Example instinct
  • where Filter rows Failed requests, specific URL, severity
  • ago() Relative time window TimeGenerated > ago(1h)
  • project Select/rename columns Keep name, resultCode, duration
  • extend Compute new columns bin(TimeGenerated, 5m) already via summarize, or custom flags
  • summarize Aggregate count() , avg(duration) , percentile()
  • join Combine tables Requests with traces on operation_Id
  • order by Sort Slowest first
  • take Limit output First 50 rows while exploring
  • Essential Application Insights tables

When using workspace-based Application Insights, queries often target the App* schema:

  • Table Contents AI pipeline use
  • AppRequests Inbound operations API latency, result codes, success flags
  • AppDependencies Outbound calls Model endpoint, Postgres, Redis, Service Bus
  • AppTraces Trace/log messages Custom diagnostics, OTel-related log lines
  • AppExceptions Exceptions SDK failures, deserialization errors
  • AppEvents Custom events Business markers (retrieval_miss, safety_block)
  • AppMetrics Metric samples Token counts, queue lengths (metrics focus expands in 12.4)

Classic names like requests / dependencies appear in some Application Insights query scopes; on exam stems, read the table names given and apply the same operators.

Container and platform logs

Not every clue lives in App* . Container stdout/stderr and platform logs matter when instrumentation is incomplete or the process crashes before exporting spans.

  • Table (examples) Typical signal
  • ContainerAppConsoleLogs_CL Console output from Container Apps revisions
  • ContainerAppSystemLogs_CL Platform/system events for Container Apps
  • AppServiceConsoleLogs / similar App Service container console (when used)
  • AzureDiagnostics Broad diagnostic settings output (service-dependent)

Pattern: if Application Insights is empty but the container is restarting, query console logs for crash stacks; if App Insights has requests but dependencies look wrong, stay in AppDependencies and fix instrumentation.

  • Time filtering with ago
  • Always constrain time. Unbounded queries are slow and noisy. AppRequests
  • | where TimeGenerated > ago(2h)
  • | where Success == false
  • | project TimeGenerated, Name, ResultCode, DurationMs, OperationId=operation_Id, Url
  • | order by TimeGenerated desc
  • | take 50

Notes for exam reading:

  • ago(2h) means “two hours before now”
  • Prefer TimeGenerated (ingestion/event time field commonly used in Log Analytics)
  • Success == false quickly finds failing requests; also filter ResultCode >= 500 when Success semantics differ
  • project vs summarize

project shapes rows without collapsing them—ideal when you need individual failing operations.

  • summarize collapses rows—ideal for rates and top offenders: AppRequests
  • | where TimeGenerated > ago(24h)
  • | summarize Failures=countif(Success == false), Total=count(), AvgDuration=avg(DurationMs) by Name
  • | extend FailureRate = todouble(Failures) / Total
  • | order by FailureRate desc

This answers “which operation name is hurting us?” rather than listing every row.

  • join on operation_Id
  • Correlation shines with joins: let failed = AppRequests
  • | where TimeGenerated > ago(1h)
  • | where Success == false
  • | project operation_Id, ReqName=Name, ResultCode, DurationMs;
  • AppTraces
  • | where TimeGenerated > ago(1h)
  • | join kind=inner failed on operation_Id
  • | project TimeGenerated, ReqName, ResultCode, Message, SeverityLevel
  • | order by TimeGenerated asc

Interpretation skills:

  • inner join keeps only matching IDs (good when you already have failed requests)
  • leftouter keeps requests even when traces are missing (detects instrumentation gaps)
  • Always project a lean column set so results stay readable

You can similarly join AppRequests to AppDependencies to see which outbound call failed inside a request.

  • Searching messages and custom dimensions
  • Traces often need text filters: AppTraces
  • | where TimeGenerated > ago(6h)
  • | where Message has "rate limit" or Message has "429"
  • | summarize count() by bin(TimeGenerated, 15m)

has is a practical token search. For structured custom dimensions, use dynamic accessors as shown in portal samples (stems usually give the field path).

Scenario: finding a bad Container Apps revision

Asha deployed revision chat-api--v12 . Error rates climbed. She runs:

  • AppRequests summarize failures by cloud role / app version dimensions available in her schema
  • ContainerAppConsoleLogs_CL filtered to the revision name for panic/OOM lines
  • Join failed operation_Id values to AppExceptions

She finds exceptions only on v12 referencing a missing environment variable for the model deployment name. Rollback to the prior revision restores health. KQL made the revision association explicit.

  • Query hygiene for exams and incidents
  • Start narrow (time + failure), then widen
  • Name columns clearly when joining ( ReqName vs Message )
  • Beware of high-cardinality summarize by fields (raw URLs with IDs)
  • Confirm you are in the correct workspace connected to the Application Insights resource
  • Remember legal/privacy limits: do not dump full prompts in shared query results if policy forbids it
  • Practice shapes to memorize
  • Goal Starting shape
  • Recent failures AppRequests | where ago | where not Success | project …
  • Top slow calls AppRequests | summarize percentile(DurationMs, 95) by Name
  • Logs for one operation filter operation_Id == "…" across AppTraces/AppExceptions
  • Container crash text ContainerAppConsoleLogs_CL | where ago | where Log_s has "Traceback"
  • Dependency errors AppDependencies | where Success == false | summarize count() by Target, ResultCode

Master these patterns before section 12.4, which applies the same operators to metrics-heavy and multi-stage AI troubleshooting scenarios.