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.