Skip to content
CAMPUX
Field notes · Observability
KQL for beginners

KQL for beginners: read your Azure logs in an afternoon

By 6 min read

Kusto Query Language looks intimidating until you see the trick: you start with a table and push it through a pipe, one small operator at a time. Learn five of them and you can answer real questions about your Azure environment today.

New to cloud? CAMPUX is a free, build-first course. Start here →

Every serious Azure environment pours its telemetry — VM performance, sign-ins, security events, application traces — into Azure Monitor Logs, and the only way to ask that ocean a question is KQL, the Kusto Query Language. The good news the tutorials bury: KQL is one of the friendliest query languages you will ever meet, because it reads left to right like a sentence. If you have ever used a Unix pipe, you already understand the whole model.

The one idea: a table, then a pipe

A KQL query starts with a table name and then pushes that data through a series of operators, each separated by the pipe character |. As the docs describe it, "the output of the first command is the input of the next," and you can chain as many as you like. Start with the simplest possible query:

SecurityEvent
| take 10

That says: take the SecurityEvent table, and give me any 10 rows. It is how you peek at a table's shape before you know anything about it. Everything else in KQL is just adding more pipes to refine what comes out the end. That is the entire mental model — hold onto it and the rest is vocabulary.

A KQL query is a pipeline: start from a table, then pipe through where, summarize, and render.AzureActivitythe table| wherefilter rows| summarizeaggregate| renderchart ita query reads top-to-bottom, piped with |learn these four operators and you can write most Azure Monitor queries
Figure — KQL reads like a pipeline. You start from a table and pipe it through operators with the | symbol: where filters the rows, summarize aggregates them, render draws a chart. Each stage takes the previous stage’s output, so you build a query by stacking small, readable steps rather than one dense expression.

Operator 1 — where: keep only the rows you care about

Filtering is the workhorse. where keeps rows that match a condition, and you can pipe several together:

SecurityEvent
| where Level == 8
| where EventID == 4672

One note that saves beginners an hour of confusion: KQL is case-sensitive. Keywords are lowercase, and table and column names must match the schema exactly. SecurityEvent works; securityevent does not.

Operator 2 — time, the filter you will use most

Log data is enormous, so nearly every good query bounds its time range early, right after the table name, so everything downstream works on less data:

SecurityEvent
| where TimeGenerated > ago(1h)

ago(1h) means "one hour ago," so this keeps only the last hour. Swap in ago(7d) for a week or ago(30m) for thirty minutes. Putting the time filter first is not just tidy — it makes the query faster, because every later operator has less to chew on.

Operator 3 — project: choose your columns

Tables can have dozens of columns; project trims the result to the ones you want (and can rename or compute new ones):

SecurityEvent
| where TimeGenerated > ago(1h)
| project TimeGenerated, Computer, Activity

Now your results are three clean columns instead of a wall of fields. Think of project as "SELECT these columns" for anyone coming from SQL.

Operator 4 — sort and top: put it in order

take gives arbitrary rows; when order matters, use sort by, or better, top, which sorts and limits in one step:

SecurityEvent
| top 10 by TimeGenerated

That returns the 10 most recent events. (Descending is the default for top and sort; add asc if you want oldest first.) This is your go-to for "show me the latest N of something."

Operator 5 — summarize: the one that feels like magic

Everything so far filters or shapes individual rows. summarize is where KQL earns its reputation: it groups rows and aggregates them. The most common form is a count per group:

SecurityEvent
| where TimeGenerated > ago(1h)
| summarize count() by Account

Read it as a sentence: "of the last hour of security events, count the rows for each account." One line just turned a million raw events into a ranked tally of who did what. You can aggregate other ways too — avg(), max(), min() — and group by more than one column.

The finishing move is grouping by time, using bin() to bucket a continuous timestamp into intervals — the basis of every time chart you have seen in Azure:

Perf
| where TimeGenerated > ago(7d)
| where CounterName == "Available MBytes"
| summarize avg(CounterValue) by bin(TimeGenerated, 1h)

That computes average free memory per hour over a week — switch the result to a line chart and you have a graph. That is genuinely most of what day-to-day monitoring queries do.

Start with a table. Add one pipe at a time. Stop when the question is answered.

The learning method that works

Do not memorise operators — build queries up one pipe at a time in Log Analytics. Run the table alone, look at the output, add a where, run it again, add a project, run it again. Because each pipe just transforms the previous result, you can always see exactly what each line did. KQL is discoverable in a way most query languages are not.

You now know enough to be useful

Five operators — where, a time filter with ago(), project, top, and summarize … by — cover a startling share of real investigations: which VMs are low on memory, who signed in from a new country, how many errors per hour, what are the top talkers. There is far more in KQL — joins, parse, make-series, functions — but you reach for those once the basics feel natural, not before. Open Log Analytics, pick a table, type its name, and start adding pipes. In an afternoon the ocean of logs stops being scary and starts answering questions.

Questions people also ask

What is KQL used for?

KQL queries the telemetry Azure Monitor Logs collects: VM performance counters, sign-in records, security events, and application traces. You write a query to filter, shape, and aggregate that data so you can answer a specific question, such as which accounts logged in during the last hour or which VMs are low on memory.

Is KQL the same as SQL?

No. KQL is read-only and built around a pipe: a table name feeds into a chain of operators, each one transforming the output of the one before it. SQL nests SELECT statements; KQL reads left to right, so a query like table, then where, then project reads like a sentence instead of a nested clause.

Is KQL easy to learn?

Yes, if you already understand a Unix pipe. Five operators cover most real investigations: where, a time filter with ago(), project, top, and summarize by. You build a query one pipe at a time in Log Analytics, running it after each addition, so you always see what the last operator changed.

What does the pipe symbol do in KQL?

The pipe character | sends the output of one operator into the next as input. A query starts with a table name and each pipe narrows or reshapes the result, so where filters rows, project trims columns, and summarize groups and aggregates what is left, all in a single left-to-right chain.

Why is the where clause case-sensitive in KQL?

KQL keeps table and column names matched exactly to the underlying schema, so SecurityEvent works but securityevent does not. This trips up beginners coming from SQL, where case usually does not matter. Keywords like where and project stay lowercase, but the names of tables and columns must match the schema's casing.

Further reading — the Microsoft docs
Your next class · free
You've read the idea. Class 29 — KQL Essentials is where you build it, hands-on — no account needed.Start Class 29 →
Captain O
Founder & instructor · CAMPUX Cloud Engineering Bootcamp
LinkedIn
Drilled in Class 29 — KQL Essentials. Next note: What is an Azure landing zone? →