KQL for beginners: read your Azure logs in an afternoon
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.
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.
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.
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.